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
- Добавить поддержку
?
- Очистка кода