Python中最佳维护的通用函数实现是什么?

8 投票
3 回答
1433 浏览
提问于 2025-04-15 14:25

一个通用函数是根据它所有参数的类型来决定使用哪个版本的函数。程序员可以定义多个函数的实现,然后在调用时根据参数的类型选择正确的版本。这种方式在对象适配等方面非常有用。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

我最近想到的一个更好的例子是一个需要用户的网页组件。它不需要特定的网页框架,集成者只需要写类似这样的代码:

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)。程序员可以注册一个可调用的适配器,这个适配器接受特定类的对象作为参数,并返回与接口兼容的东西。这是一种很有用的方式,可以支持插件功能。与猴子补丁不同,它即使在一个对象需要适配多个接口且方法名相同的情况下也能正常工作。

3 个回答

0

你可以使用这样的写法:

def my_func(*args, **kwargs):
    pass

在这个例子中,args会是一个包含所有未命名参数的列表,而kwargs则是一个包含命名参数的字典。通过这些,你可以检查它们的类型,然后做出相应的处理。

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()
7

我推荐P. Eby开发的PEAK-Rules库。还有一个同作者的库叫RuleDispatch(不过这个已经不再维护了),它是PEAK-Rules的前身。

PEAK-Rules有很多不错的功能,其中一个是它可以扩展(虽然不是特别简单)。除了传统的只根据类型进行分发外,它还支持根据任意表达式进行分发,称为“守护者”。

len()函数并不是真正的通用函数(至少在上面提到的库的意义上,以及在像Common LispDylanCecil这样的语言中使用的意义上),因为它只是一个方便的语法,用于调用特别命名的(但其他方面是常规的)方法:

len(s) == s.__len__()

还要注意,这只是单一分发,也就是说,实际的接收者(上面代码中的s)决定了调用哪个方法实现。即使是一个假设的

def call_special(receiver, *args, **keys):
    return receiver.__call_special__(*args, **keys)

仍然是一个单一分发函数,因为在解析要调用的方法时,只使用接收者。其余的参数只是被传递,但不会影响方法的选择。

这与多重分发不同,在多重分发中没有专门的接收者,所有参数都会被用来找到实际要调用的方法实现。这才是整个事情的价值所在。如果它只是某种奇怪的语法糖,没人会愿意使用它,我觉得。

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中通用函数的一个很好的使用场景,我推荐阅读peak.security的重构代码,它提供了一种非常优雅的解决方案,用于通过通用函数进行访问权限检查(使用RuleDispatch)。

撰写回答