Как python обрабатывает сценарии универсального / шаблонного типа? Скажем, я хочу создать внешний файл «BinaryTree.py» и заставить его обрабатывать двоичные деревья, но для любого типа данных.
Поэтому я мог бы передать ему тип настраиваемого объекта и получить двоичное дерево этого объекта. Как это делается в питоне?
Ответы:
Python использует утиную типизацию , поэтому ему не нужен специальный синтаксис для обработки нескольких типов.
Если вы знакомы с C ++, вы помните, что до тех пор, пока операции, используемые в функции / классе шаблона, определены для некоторого типа
T
(на уровне синтаксиса), вы можете использовать этот типT
в шаблоне.Итак, в основном это работает так же:
Однако вы заметите, что до тех пор, пока вы не напишете явную проверку типа (что обычно не рекомендуется), вы не сможете обеспечить, чтобы двоичное дерево содержало только элементы выбранного типа.
источник
if isintance(o, t):
илиif not isinstance(o, t):
... довольно просто.Другие ответы совершенно нормальные:
Однако, если вам по-прежнему нужен типизированный вариант, есть встроенное решение, начиная с Python 3.5.
Общие классы :
from typing import TypeVar, Generic T = TypeVar('T') class Stack(Generic[T]): def __init__(self) -> None: # Create an empty list with items of type T self.items: List[T] = [] def push(self, item: T) -> None: self.items.append(item) def pop(self) -> T: return self.items.pop() def empty(self) -> bool: return not self.items
# Construct an empty Stack[int] instance stack = Stack[int]() stack.push(2) stack.pop() stack.push('x') # Type error
Общие функции:
from typing import TypeVar, Sequence T = TypeVar('T') # Declare type variable def first(seq: Sequence[T]) -> T: return seq[0] def last(seq: Sequence[T]) -> T: return seq[-1] n = first([1, 2, 3]) # n has type int.
Ссылка: документация mypy о дженериках .
источник
На самом деле теперь вы можете использовать дженерики в Python 3.5+. См. Документацию по PEP-484 и модулю набора текста .
Согласно моей практике, это не очень гладко и понятно, особенно для тех, кто знаком с Java Generics, но все же можно использовать.
источник
Придумав несколько хороших мыслей о создании универсальных типов в 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 )
как родительские, будут наследовать от двух отдельных классов. Чтобы преодолеть это, вам нужно создать команду для хранения различных форм внутреннего класса и возврата ранее созданного внутреннего класса, а не создания нового. Это предотвратит создание повторяющихся типов с одинаковыми параметрами. Если интересно, можно сделать более элегантное решение с помощью декораторов и / или метаклассов.источник
Поскольку 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)
источник
foo
для каждого объекта, то размещение строк в контейнере - плохая идея. Принимать что-либо - не лучшая идея . Однако удобно не требовать, чтобы все объекты в контейнере были производными от класса .HasAFooMethod
Поскольку python динамически типизирован, это очень просто. Фактически, вам придется проделать дополнительную работу, чтобы ваш класс BinaryTree не работал с каким-либо типом данных.
Например, если вам нужны ключевые значения, которые используются для размещения объекта в дереве, доступном внутри объекта из метода, подобного тому, как
key()
вы просто вызываетеkey()
объекты. Например:class BinaryTree(object): def insert(self, object_to_insert): key = object_to_insert.key()
Обратите внимание, что вам никогда не нужно определять, что это за класс object_to_insert. Пока у него есть
key()
метод, он будет работать.Исключение составляют случаи, когда вы хотите, чтобы он работал с основными типами данных, такими как строки или целые числа. Вам нужно будет обернуть их в класс, чтобы заставить их работать с вашим универсальным BinaryTree. Если это звучит слишком тяжело, и вам нужна дополнительная эффективность, заключающаяся в том, чтобы просто хранить строки, извините, это не то, в чем Python хорош.
источник
Integer
упаковкой / распаковкой).Вот вариант этого ответа, который использует метаклассы, чтобы избежать беспорядочного синтаксиса, и использует синтаксис
typing
-styleList[int]
:class template(type): def __new__(metacls, f): cls = type.__new__(metacls, f.__name__, (), { '_f': f, '__qualname__': f.__qualname__, '__module__': f.__module__, '__doc__': f.__doc__ }) cls.__instances = {} return cls def __init__(cls, f): # only needed in 3.5 and below pass def __getitem__(cls, item): if not isinstance(item, tuple): item = (item,) try: return cls.__instances[item] except KeyError: cls.__instances[item] = c = cls._f(*item) item_repr = '[' + ', '.join(repr(i) for i in item) + ']' c.__name__ = cls.__name__ + item_repr c.__qualname__ = cls.__qualname__ + item_repr c.__template__ = cls return c def __subclasscheck__(cls, subclass): for c in subclass.mro(): if getattr(c, '__template__', None) == cls: return True return False def __instancecheck__(cls, instance): return cls.__subclasscheck__(type(instance)) def __repr__(cls): import inspect return '<template {!r}>'.format('{}.{}[{}]'.format( cls.__module__, cls.__qualname__, str(inspect.signature(cls._f))[1:-1] ))
С помощью этого нового метакласса мы можем переписать пример в ответе, на который я ссылаюсь, как:
@template def List(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 l = List[int]() l.append(1) # ok l.append("one") # error
У этого подхода есть хорошие преимущества
print(List) # <template '__main__.List[member_type]'> print(List[int]) # <class '__main__.List[<class 'int'>, 10]'> assert List[int] is List[int] assert issubclass(List[int], List) # True
источник
Посмотрите, как это делают встроенные контейнеры.
dict
иlist
так далее, содержат разнородные элементы любых типов. Если вы определите, скажем,insert(val)
функцию для своего дерева, она в какой-то момент сделает что-то вроде,node.value = val
а Python позаботится обо всем остальном.источник
К счастью, были предприняты некоторые усилия по универсальному программированию на Python. Есть библиотека: родовая
Вот документация к нему: http://generic.readthedocs.org/en/latest/
Он не прогрессировал годами, но вы можете иметь приблизительное представление о том, как использовать и создавать свою собственную библиотеку.
Ура
источник
Если вы используете 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
поддержку?
поддержкуисточник