使用户可以扩展功能的正确方法

blu*_*rni 6 python mpmath

Python中允许用户扩展函数可以在不改变函数原始代码的情况下运行的类型的正确方法是什么?

假设我有一个模块,其my_module.foo()函数最初是为了处理float类型而编写的.现在我希望同样的功能能够使用,也就是说,mpmath任意精度浮点数 - 但不改变原始模块中的代码.

在C++中,我会添加一个额外的重载(或者,更可能的是,一些模板特化技巧与一个辅助结构).我应该如何构造原始my_module.foo()代码,以便用户可以在其中添加自己的自定义钩子?

我可以想到一些方法来实现这一点,但作为一个新手Python程序员,我相信他们中的大多数都会变得可怕:)

编辑:感谢所有答案到目前为止,非常感谢.

我应该澄清一个关键要求是能够应对我自己没有定义的类型.例如,如果我想编写一个通用的cos我的模块中的功能,我想打电话给math.cos在内置的类型,mpmath.cosmpf类型sympy.cos上sympy象征性的类型,等等.当然我希望调度逻辑不是在我模块的cos实现.

Tho*_*zco 7

有两种方法可以做到这一点:

  • 代表团.已经在Python中了.大多数Pythonic.不适用于内置类型.不完全是你想要的.
  • 单一调度.还是一个PEP.适用于内置类型.确切地说,你正在寻找什么.

代表团

您通常将责任委托给您正在执行的对象,并且您没有在函数中实现逻辑.

这是一个例子:len.实施len非常明确:

def len(obj):
    return obj.__len__()
Run Code Online (Sandbox Code Playgroud)

不同类型的(str,list,tuple...)有不同的实现,但它们具有相同功能的所有工作.

现在,如果我想定义我自己的类型,可以使用len,我可以这样做:

class MyTypeOfLength3(object):
    def __len__(self):
        return 3

o = MyTypeOfLength3()
print len(o) 
# 3
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你将实现一些看起来像len.

(注意:这不是实际代码len,但它或多或少相当.)

单一调度

当然,在某些情况下,这可能不切实际.如果那是你的情况,那么"单一调度"PEP 443可能正是你所寻找的.

它建议一个新的装饰器,可以完成你正在寻找的东西:

>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)
...
>>> @fun.register(int)
... def _(arg, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> @fun.register(list)
... def _(arg, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)
Run Code Online (Sandbox Code Playgroud)

一旦你定义了你的函数,你就可以调用fun(something),Python将找到正确的实现(intlist这里)回退到默认实现def fun(...): ....

因此,您只需要装饰您的原始功能,并且您已完成,您的用户可以添加自己的类型.

注意:正如评论中指出的那样,singledispatch已经在Python中实现了它pkgutil.simplegeneric