python如何处理通用/模板类型的场景?假设我想创建一个外部文件“ BinaryTree.py”,并使其处理二进制树,但是对于任何数据类型。
因此,我可以将自定义对象的类型传递给它,并为该对象提供一个二叉树。如何在python中完成?
Answers:
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中创建泛型类型的好主意之后,我开始寻找其他有相同想法的人,但是我找不到任何东西。所以,就在这里。我尝试了一下,效果很好。它允许我们在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
在每个对象上调用一个方法,则将字符串放入容器中是个坏主意。接受任何东西不是一个更好的主意。但是,方便的是,不需要容器中的所有对象都派生自class 。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
装箱/拆箱的Java中一样)。
这是此答案的一个变体,它使用元类来避免语法混乱,而使用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
幸运的是,已经为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)
待办事项
<? extends List<Number>>
)super
支持?
支持