根据其所有参数的类型调度generic function。程序员定义了函数的几种实现。在呼叫时根据其参数的类型选择正确的一个。这对于对象适应等很有用。 Python有一些通用函数,包括len()
。
这些软件包倾向于允许代码如下所示:
@when(int)
def dumbexample(a):
return a * 2
@when(list)
def dumbexample(a):
return [("%s" % i) for i in a]
dumbexample(1) # calls first implementation
dumbexample([1,2,3]) # calls second implementation
我最近一直在考虑的一个不那么愚蠢的例子是需要用户的Web组件。集成商不需要特定的Web框架,只需编写类似的内容:
class WebComponentUserAdapter(object):
def __init__(self, guest):
self.guest = guest
def canDoSomething(self):
return guest.member_of("something_group")
@when(my.webframework.User)
componentNeedsAUser(user):
return WebComponentUserAdapter(user)
Python有一些通用的函数实现。为什么我会选择其中一个呢?如何在应用程序中使用该实现?
我熟悉Zope的zope.component.queryAdapter(object, ISomething)
。程序员注册一个可调用的适配器,该适配器将特定类的对象作为其参数,并返回与接口兼容的东西。这是允许插件的有用方法。与猴子修补不同,即使对象需要使用相同的方法名称来适应多个接口,它也能工作。
答案 0 :(得分:7)
我推荐P. Eby的PEAK-Rules库。同一作者(虽然已弃用)是RuleDispatch包(PEAK-Rules的前身)。后者不再维持IIRC。
PEAK-Rules有许多不错的功能,其中之一就是(很好,不容易,但是)可扩展。除了类型ony的“经典”调度之外,它还将任意表达式的调度称为“监护人”。
len()
函数不是真正的泛型函数(至少在上面提到的包的意义上,从某种意义上说,这个术语在Common Lisp,{{3}等语言中使用}或Dylan),因为它只是一个方便的语法,用于调用特殊命名(但其他常规)方法:
len(s) == s.__len__()
另请注意,这只是单一调度,即实际接收器(上面代码中的s
)确定调用的方法实现。甚至是一个假设的
def call_special(receiver, *args, **keys):
return receiver.__call_special__(*args, **keys)
仍然是单调度函数,因为在解析要调用的方法时仅使用接收器。其余的参数只是传递,但它们不会影响方法选择。
这与multi-dispatch不同,后者没有专用接收器,并且所有参数都用于查找要调用的实际方法实现。这就是实际上使整个事情变得有价值的东西。如果它只是一些奇怪的语法糖,没有人会打扰使用它,恕我直言。
from peak.rules import abstract, when
@abstract
def serialize_object(object, target):
pass
@when(serialize_object, (MyStuff, BinaryStream))
def serialize_object(object, target):
target.writeUInt32(object.identifier)
target.writeString(object.payload)
@when(serialize_object, (MyStuff, XMLStream))
def serialize_object(object, target):
target.openElement("my-stuff")
target.writeAttribute("id", str(object.identifier))
target.writeText(object.payload)
target.closeElement()
在此示例中,调用
serialize_object(MyStuff(10, "hello world"), XMLStream())
考虑两个参数,以决定实际调用哪个方法。
对于Python中泛型函数的一个很好的使用场景,我建议阅读Cecil的重构代码,它为使用泛型函数(使用RuleDispatch
)访问权限检查提供了一个非常优雅的解决方案。
答案 1 :(得分:0)
你可以使用这样的结构:
def my_func(*args, **kwargs):
pass
在这种情况下,args将是任何未命名参数的列表,kwargs将是已命名参数的字典。从这里,您可以检测其类型并采取适当的行动。
答案 2 :(得分:0)
我无法看到这些“通用”功能的重点。这听起来像是简单的多态性。
您可以像这样实现“通用”功能,而无需使用任何运行时类型标识。
class intWithDumbExample( int ):
def dumbexample( self ):
return self*2
class listWithDumbExmaple( list ):
def dumpexample( self ):
return [("%s" % i) for i in self]
def dumbexample( a ):
return a.dumbexample()