python中的泛型/模板?
python如何处理泛型/模板类型的场景?假设我想创建一个外部文件“BinaryTree.py”,并让它处理二叉树,但不针对任何数据类型python中的泛型/模板?,python,templates,generic-programming,Python,Templates,Generic Programming,python如何处理泛型/模板类型的场景?假设我想创建一个外部文件“BinaryTree.py”,并让它处理二叉树,但不针对任何数据类型 因此,我可以将自定义对象的类型传递给它,并拥有该对象的二叉树。这在python中是如何做到的?看看内置容器是如何做到的dict和list等包含任意类型的异构元素。例如,如果为树定义一个insert(val)函数,它将在某个时候执行类似node.value=val的操作,而Python将处理其余部分。因为Python是动态类型的,所以对象的类型在很多情况下并不重
因此,我可以将自定义对象的类型传递给它,并拥有该对象的二叉树。这在python中是如何做到的?看看内置容器是如何做到的
dict
和list
等包含任意类型的异构元素。例如,如果为树定义一个insert(val)
函数,它将在某个时候执行类似node.value=val
的操作,而Python将处理其余部分。因为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)
因为python是动态类型的,所以这非常简单。事实上,为了使BinaryTree类不使用任何数据类型,您必须做额外的工作 例如,如果希望通过
key()
等方法获得用于将对象放置在树中的键值,则只需对对象调用key()
。例如:
class BinaryTree(object):
def insert(self, object_to_insert):
key = object_to_insert.key()
请注意,您永远不需要定义要插入的类对象的类型。只要它有一个key()
方法,它就可以工作
例外情况是,如果您希望它处理基本数据类型,如字符串或整数。您必须将它们封装在一个类中,才能让它们与通用二进制树一起工作。如果这听起来太重了,并且您想要实际存储字符串的额外效率,那么很抱歉,这不是Python所擅长的。Python使用的,因此它不需要特殊语法来处理多个类型
如果您来自C++背景,您会记得,只要模板函数/类中使用的操作在某个类型“代码> t>代码>(语法级别)上定义,就可以在模板中使用该类型<代码> t>代码> 所以,基本上,它的工作原理是一样的:
但是,您会注意到,除非编写显式类型检查(通常不鼓励这样做),否则无法强制二叉树只包含所选类型的元素。幸运的是,python中的泛型编程已经做出了一些努力。 有一个图书馆: 以下是它的文档: 这几年来没有进展,但你可以大致了解如何使用和制作自己的图书馆
干杯实际上,现在您可以在Python 3.5+中使用泛型了。 见和
根据我的实践,它不是非常无缝和清晰,特别是对于那些熟悉Java泛型但仍然可用的人来说。在提出了一些用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来存储内部类的各种形式,并返回以前创建的内部类,而不是创建一个新的内部类。这将防止创建具有相同参数的重复类型。如果感兴趣,可以使用装饰器和/或元类来创建更优雅的解决方案 如果您使用Python 2或想要重写java代码。他们并不是解决这个问题的真正办法。这是我一个晚上的工作:我仍然没有编译器,所以你现在就这样使用它:
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)
待办事项
- 编译程序
- 让泛型类和类型正常工作(对于
之类的事情,这里有一个变体,它使用元类来避免混乱的语法,并使用
类型
-style
语法:列表[int]
有了这个新的元类,我们可以将我链接到的答案中的示例重写为:类模板(类型): 定义新(元,f): cls=类型.uuu新的uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu{ "f":f,, “资格名称”:f.“资格名称”, “模块”:f.“模块”, “uuu doc”:f.uu doc__ }) cls._实例={} 返回cls 定义初始值(cls,f):#仅在3.5及以下版本中需要 通过 定义获取项目(cls,项目): 如果不是isinstance(项,元组): 项目=(项目,) 尝试: 返回cls.\u实例[项目] 除KeyError外: cls.\u实例[项目]=c=cls.\u f(*项目) item_repr='['+','.连接(repr(i)表示item中的i)+']' c、 \uuuuu名称\uuuuuu=cls.\uuuuuuuu名称\uuuuuuuuu+项目报告 c、 质量名称=cls.\uuuuu质量名称+项目报告 c、 \uuuuu模板\uuuuu=cls 返回c 定义子类检查(cls,子类): 对于子类.mro()中的c: 如果getattr(c,'.'模板'.'无)=cls: 返回真值 返回错误 定义实例检查(cls,实例): 返回cls.\uuuu子项检查\uuuuu(类型(实例)) 定义报告(cls): 进口检验 返回“”。格式(“{}.{}[{}]”。格式( cls.模块cls.质量名称str(检查签名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
打印(列表)# 打印(列表[int])# 断言列表[int]是列表[int] 断言issubclass(列表[int],L
from typing import TypeVar, Generic, List 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.
python3 -m pip install mypy
mypy foo.py
mypy some_directory
foo.py:23: error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int"