简单的泛型函数(类似于python自己的len()、pickle.dump()等)
simplegeneric的Python项目详细描述
- 0.8中的新特性:源代码和测试与python 3兼容(不带setup.py)
- 0.8.1:setup.py现在也与python 3兼容
- 0.7中的新功能:Multiple Types or Objects
- 0.6中的新功能:Inspection and Extension,以及线程安全方法注册
simplegeneric模块允许您定义简单的单分派 泛型函数,类似于Python的内置泛型函数,比如 len(),iter()等等但是,不是使用 特别命名的方法,这些泛型函数使用简单的查找 表,类似于由例如pickle.dump()和其他 在python标准库中找到的泛型函数。
从上面的例子可以看出,泛型函数实际上是 在Python中已经很常见了,但是没有标准的方法来创建 简单的这个库试图填补这个空白,作为泛型 函数是一个excellent alternative to the Visitor pattern,如 以及作为适应的最常用的替代品
这个库试图成为泛型 函数,因此它避免使用多个或谓词 调度,以及避免加速技术,如c调度 或者代码生成。但它绝对没有依赖性,除了 python 2.4,实现只是 少于100行
用法
定义和使用泛型函数很简单:
>>> from simplegeneric import generic >>> @generic ... def move(item, target): ... """Default implementation goes here""" ... print("what you say?!") >>> @move.when_type(int) ... def move_int(item, target): ... print("In AD %d, %s was beginning." % (item, target)) >>> @move.when_type(str) ... def move_str(item, target): ... print("How are you %s!!" % item) ... print("All your %s are belong to us." % (target,)) >>> zig = object() >>> @move.when_object(zig) ... def move_zig(item, target): ... print("You know what you %s." % (target,)) ... print("For great justice!") >>> move(2101, "war") In AD 2101, war was beginning. >>> move("gentlemen", "base") How are you gentlemen!! All your base are belong to us. >>> move(zig, "doing") You know what you doing. For great justice! >>> move(27.0, 56.2) what you say?!
继承和允许的类型
为同一类型或对象定义多个方法是错误的:
>>> @move.when_type(str) ... def this_is_wrong(item, target): ... pass Traceback (most recent call last): ... TypeError: <function move...> already has method for type <...'str'> >>> @move.when_object(zig) ... def this_is_wrong(item, target): pass Traceback (most recent call last): ... TypeError: <function move...> already has method for object <object ...>
而when_type()decorator只接受类或类型:
>>> @move.when_type(23) ... def move_23(item, target): ... print("You have no chance to survive!") Traceback (most recent call last): ... TypeError: 23 is not a type or class
为超类型定义的方法按照MRO顺序继承:
>>> class MyString(str): ... """String subclass""" >>> move(MyString("ladies"), "drinks") How are you ladies!! All your drinks are belong to us.
也支持经典类实例(尽管查找过程 比新样式实例慢):
>>> class X: pass >>> class Y(X): pass >>> @move.when_type(X) ... def move_x(item, target): ... print("Someone set us up the %s!!!" % (target,)) >>> move(X(), "bomb") Someone set us up the bomb!!! >>> move(Y(), "dance") Someone set us up the dance!!!
多种类型或对象
为了方便起见,现在可以将多个类型或对象传递给 注册方法:
>>> @generic ... def isbuiltin(ob): ... return False >>> @isbuiltin.when_type(int, str, float, complex, type) ... @isbuiltin.when_object(None, Ellipsis) ... def yes(ob): ... return True >>> isbuiltin(1) True >>> isbuiltin(object) True >>> isbuiltin(object()) False >>> isbuiltin(X()) False >>> isbuiltin(None) True >>> isbuiltin(Ellipsis) True
默认值和文档
可以使用函数的^{tt7}获取函数的默认实现$ 属性:
>>> @move.when_type(Y) ... def move_y(item, target): ... print("Someone set us up the %s!!!" % (target,)) ... move.default(item, target) >>> move(Y(), "dance") Someone set us up the dance!!! what you say?!
help()和其他文档工具将泛型函数视为普通函数 函数对象,具有与 原型/默认功能:
>>> help(move) Help on function move: ... move(*args, **kw) Default implementation goes here ...
检查和扩展
您可以使用 has_object()和has_type()方法:
>>> move.has_object(zig) True >>> move.has_object(42) False >>> move.has_type(X) True >>> move.has_type(float) False
注意,has_type()只查询是否有注册的方法 exact类型,而不是子类型或父类型:
>>> class Z(X): pass >>> move.has_type(Z) False>您可以创建一个泛型函数,该函数从现有泛型继承 通过调用现有函数上的函数来执行函数:
>>> move2 = generic(move) >>> move(2101, "war") In AD 2101, war was beginning.
添加到新泛型函数的任何方法都重写 “基本”功能:
>>> @move2.when_type(X) ... def move2_X(item, target): ... print("You have no chance to survive make your %s!" % (target,)) >>> move2(X(), "time") You have no chance to survive make your time! >>> move2(Y(), "time") You have no chance to survive make your time!
注意,即使move()有一个类型为Y的方法,该方法 为move2()中的X定义优先。这是因为 move函数用作move2和^{tt19}的default方法$ 没有类型Y:
的方法>>> move2.default is move True >>> move.has_type(Y) True >>> move2.has_type(Y) False
限制
- 第一个参数始终用于分派,并且必须始终是 调用函数时按位置传递了。
- 文档工具看不到函数的原始参数签名,因此 你必须在文档字符串中描述它。
- 如果有可选参数,则必须在 命令它们正常工作。(从好的方面来说,这意味着你可以 尽管依赖于 这种怪癖可能不是个好主意。)
如果我觉得有必要,这些限制可能会在以后的版本中取消。他们 需要像我这样生成运行时代码RuleDispatch, 不过,这有点痛苦。(或者我可以使用 BytecodeAssembler包来生成代码,因为这样做容易得多 使用基于字符串的代码生成,但这将引入更多 依赖关系,我尽量保持简单,这样我就可以 把它扔到钱德勒身上,不增加很大的脚印。)