Generics/templates в python?

Как python обрабатывает сценарии типа generic/template type? Скажем, я хочу создать внешний файл "BinaryTree.py" и обработать бинарные деревья, но для любого типа данных.

Поэтому я мог бы передать ему тип настраиваемого объекта и иметь двоичное дерево этого объекта. Как это делается в python?

Ответ 1

Python использует duck typing, поэтому для обработки нескольких типов не требуется специальный синтаксис.

Если вы работаете на фоне С++, вы помните, что, если операции, используемые в функции/классе шаблона, определены на некотором типе T (на уровне синтаксиса), вы можете использовать этот тип T в шаблоне.

Итак, в основном, он работает одинаково:

  • определить контракт для типа элементов, которые вы хотите вставить в двоичное дерево.
  • документируйте этот контракт (т.е. в документации по классу)
  • реализовать двоичное дерево, используя только операции, указанные в контракте
  • пользоваться

Однако вы заметите, что, если вы не пишете явную проверку типов (которая обычно не рекомендуется), вы не сможете обеспечить, чтобы двоичное дерево содержало только элементы выбранного типа.

Ответ 2

На самом деле теперь вы можете использовать generics в Python 3.5+. См. PEP-484 и ввод документации по библиотеке.

В соответствии с моей практикой это не очень легко и ясно, особенно для тех, кто знаком с Java Generics, но все же можно использовать.

Ответ 3

После того, как я придумал несколько хороших мыслей о создании универсальных типов в python, я начал искать тех, у кого была такая же идея, но я не смог найти ни одной. Итак, вот оно. Я попробовал это, и это работает хорошо. Это позволяет нам параметризировать наши типы в python.

class List( type ):

    def __new__(type_ref, member_type):

        class List(list):

            def append(self, member):
                if not isinstance(member, member_type):
                    raise TypeError('Attempted to append a "{0}" to a "{1}" which only takes a "{2}"'.format(
                        type(member).__name__,
                        type(self).__name__,
                        member_type.__name__ 
                    ))

                    list.append(self, member)

        return List 

Теперь вы можете получать типы из этого универсального типа.

class TestMember:
        pass

class TestList(List(TestMember)):

    def __init__(self):
        super().__init__()


test_list = TestList()
test_list.append(TestMember())
test_list.append('test') # This line will raise an exception

Это решение упрощенное и имеет ограничения. Каждый раз, когда вы создаете универсальный тип, он создает новый тип. Таким образом, несколько классов, наследующих List( str ) в качестве родителя, будут наследоваться от двух отдельных классов. Чтобы преодолеть это, вам нужно создать dict для хранения различных форм внутреннего класса и вернуть ранее созданный внутренний класс, а не создавать новый. Это предотвратит создание дубликатов типов с одинаковыми параметрами. Если интересно, более элегантное решение может быть сделано с помощью декораторов и/или метаклассов.

Ответ 4

Поскольку python динамически типизирован, это очень просто. На самом деле вам придется выполнять дополнительную работу для вашего класса BinaryTree, чтобы не работать с каким-либо типом данных.

Например, если вам нужны значения ключей, которые используются для размещения объекта в дереве, доступном внутри объекта, с помощью метода типа key(), вы просто вызываете key() для объектов. Например:

class BinaryTree(object):

    def insert(self, object_to_insert):
        key = object_to_insert.key()

Обратите внимание, что вам никогда не нужно определять класс класса object_to_insert. Пока он имеет метод key(), он будет работать.

Исключением является то, что вы хотите, чтобы он работал с базовыми типами данных, такими как строки или целые числа. Вам придется обернуть их в класс, чтобы заставить их работать с вашим общим BinaryTree. Если это звучит слишком тяжело, и вам нужна дополнительная эффективность на самом деле только для хранения строк, извините, это не то, на что Python хорош.

Ответ 5

Поскольку Python динамически типизирован, типы объектов не имеют большого значения во многих случаях. Это лучшая идея принять что-нибудь.

Чтобы продемонстрировать, что я имею в виду, этот древовидный класс примет что-либо для своих двух ветвей:

class BinaryTree:
    def __init__(self, left, right):
        self.left, self.right = left, right

И его можно использовать следующим образом:

branch1 = BinaryTree(1,2)
myitem = MyClass()
branch2 = BinaryTree(myitem, None)
tree = BinaryTree(branch1, branch2)

Ответ 6

Посмотрите, как это делают встроенные контейнеры. dict и list и т.д. содержат гетерогенные элементы любых типов. Если вы определяете, скажем, функцию insert(val) для вашего дерева, она в какой-то момент сделает что-то вроде node.value = val, а Python позаботится об остальном.

Ответ 7

К счастью, были предприняты некоторые усилия для общего программирования в python. Существует библиотека: generic

Вот документация для него: http://generic.readthedocs.org/en/latest/

Он не прогрессирует в течение многих лет, но вы можете иметь общее представление о том, как использовать и создавать свою собственную библиотеку.

Приветствия

Ответ 8

Если вы используете Python 2 или хотите переписать Java-код. Их не реальное решение для этого. Вот что я получаю за ночь: https://github.com/FlorianSteenbuck/python-generics У меня все еще нет компилятора, поэтому вы сейчас используете его вот так:

class A(GenericObject):
    def __init__(self, *args, **kwargs):
        GenericObject.__init__(self, [
            ['b',extends,int],
            ['a',extends,str],
            [0,extends,bool],
            ['T',extends,float]
        ], *args, **kwargs)

    def _init(self, c, a, b):
        print "success c="+str(c)+" a="+str(a)+" b="+str(b)

Todos

  • Компилятор
  • Приведите в действие универсальные классы и типы (для таких вещей, как <? extends List<Number>>)
  • Добавить поддержку super
  • Добавить поддержку ?
  • Очистка кода