【问题标题】:What is the best-maintained generic functions implementation for Python?什么是 Python 维护得最好的泛型函数实现?
【发布时间】:2009-09-18 14:54:28
【问题描述】:

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)。程序员注册一个可调用适配器,该适配器将特定类的对象作为其参数并返回与接口兼容的内容。这是允许插件的有用方法。与猴子补丁不同,即使一个对象需要适应具有相同方法名称的多个接口,它也可以工作。

【问题讨论】:

  • len 函数只是调用对象的__len__ 方法。这就是您所说的“通用”功能吗?如果是这样,那么具有这样行为的函数相对较少。你有别的想法吗?如果是这样,请选择一个更好的例子。
  • 你说的是多态性吗?或者您是在谈论猴子修补现有类型以添加​​多态性?
  • 看起来(基于 ? 和提供的维基百科链接)joeforker 正在寻求实现重载功能。我相信他指的是“通用”,因为该函数由 1 个名称调用,但根据传递的参数执行不同的代码。因此: my_function(a) -> execute function_a my_function(b) -> execute function_b 这是解析 **kwargs 以获取要执行的代码的更时尚的实现

标签: python generics


【解决方案1】:

我推荐 P. Eby 的 PEAK-Rules 库。 RuleDispatch 包(PEAK-Rules 的前身)出自同一作者(尽管已弃用)。后者不再维护 IIRC。

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

【讨论】:

  • AFAIK 这是一个非常简单的实现,它只执行基于类型的调度。我还没有真正研究它,所以我不能说什么。我的经验主要来自RuleDispatch 及其继任者。
  • simplegeneric 要简单得多,但我认为,它不像更复杂和更丰富的 PEAK 那样积极维护。
【解决方案2】:

你可以使用这样的结构:

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

在这种情况下,args 将是任何未命名参数的列表,而 kwargs 将是已命名参数的字典。从这里您可以检测它们的类型并采取适当的行动。

【讨论】:

  • 这在修改后的问题的上下文中没有意义。
【解决方案3】:

我看不出这些“通用”函数的意义所在。这听起来像是简单的多态性。

您的“通用”功能可以这样实现,而无需借助任何运行时类型识别。

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

【讨论】:

  • 您的方法不允许根据 OP 的规范调用 dumbexample(1)(并且所有通用函数包都允许)——您需要对每个此类调用进行繁琐的包装,通常需要根据类型进行切换(EP)。但是基于 ONE 类型的多态性仍然大致可以:经典的 OOP 完全崩溃和泛型函数(如 Common Lisp、Dylan 等)真正闪耀的地方在于基于 MULTIPLE 参数类型的调度(避免诸如访问者之类的笨拙,radd vs add,以及 OOP 需要的大量其他 kludges)。
  • @Alex Martelli:而且,大概是避免了强制问题和双重调度问题。 OP的问题 - 以及示例 - 没有涉及任何这些。我仍然不清楚 real 的问题。或者这个问题只是假设性的。
猜你喜欢
  • 2010-10-17
  • 2018-08-10
  • 2015-04-05
  • 2010-09-13
  • 1970-01-01
  • 2022-01-18
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多