Дженерики / шаблоны в питоне? - PullRequest
58 голосов
/ 17 июля 2011

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

Таким образом, я мог передать ему тип пользовательского объекта и получить двоичное дерево этого объекта.Как это делается в Python?

Ответы [ 8 ]

59 голосов
/ 17 июля 2011

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

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

Итак, в принципе, это работает так же:

  1. определяет контракт для типа элементов, которые вы хотите вставить в двоичное дерево.
  2. документирование этого контракта (т.е. в документации класса)
  3. реализовать двоичное дерево, используя только операции, указанные в контракте
  4. наслаждаться

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

15 голосов
/ 17 сентября 2015

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

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

7 голосов
/ 28 марта 2016

После того, как я придумал несколько хороших мыслей о создании универсальных типов в 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 для хранения различных форм внутреннего класса и вернуть ранее созданный внутренний класс, а не создавать новый.Это предотвратит создание дубликатов типов с одинаковыми параметрами.Если интересно, более элегантное решение может быть сделано с помощью декораторов и / или метаклассов.

3 голосов
/ 17 июля 2011

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

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

class BinaryTree(object):

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

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

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

2 голосов
/ 17 июля 2011

Поскольку 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)
1 голос
/ 18 сентября 2018

Если вы используете 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 поддержки
  • Добавление ? поддержки
  • Очистка кода
1 голос
/ 25 октября 2014

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

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

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

Ура

1 голос
/ 17 июля 2011

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

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