如何将 functools.singledispatch 与实例方法一起使用? [英] How can I use functools.singledispatch with instance methods?

查看:18
本文介绍了如何将 functools.singledispatch 与实例方法一起使用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Python 3.4 添加 使用静态方法定义函数重载的能力.这基本上是文档中的示例:

from functools import singledispatch类测试类(对象):@singledispatchdef test_method(arg,verbose=False):如果冗长:print("让我说吧,", end="")打印(参数)@test_method.register(int)定义_(参数):print("数字强度,嗯?", end="")打印(参数)@test_method.register(list)定义_(参数):打印(枚举这个:")对于我,枚举(arg)中的 elem:打印(我,元素)如果 __name__ == '__main__':TestClass.test_method(55555)TestClass.test_method([33, 22, 11])

在最纯粹的形式中,singledispatch 实现依赖于第一个参数来识别类型,因此很难将此功能扩展到实例方法.

有人对如何使用(或 jerry-rig)此功能使其与实例方法一起使用有任何建议吗?

解决方案

更新:从 Python 3.8 开始,functools.singledispatchmethod 允许对方法、类方法、抽象方法、和静态方法.

对于较旧的 Python 版本,请参阅此答案的其余部分.

查看singledispatch,我们可以看到装饰器返回一个函数wrapper(),它根据args[0] ...

 def wrapper(*args, **kw):return dispatch(args[0].__class__)(*args, **kw)

... 这对于普通函数来说很好,但对于实例方法用处不大,它的第一个参数总是 self.

然而,我们可以编写一个新的装饰器 methdispatch,它依赖于 singledispatch 来完成繁重的工作,而是返回一个包装函数来选择要注册的函数基于 args[1] 的类型调用:

from functools import singledispatch, update_wrapper定义方法调度(函数):调度员 = singledispatch(func)def 包装器(*args, **kw):返回 dispatcher.dispatch(args[1].__class__)(*args, **kw)wrapper.register = dispatcher.register更新包装器(包装器,功能)返回包装器

这是一个使用中的装饰器的简单示例:

class Patchwork(object):def __init__(self, **kwargs):对于 kwargs.items() 中的 k, v:setattr(self, k, v)@methdispatchdef get(self, arg):返回 getattr(self, arg, None)@get.register(列表)def _(self, arg):返回 [self.get(x) for x in arg]

请注意,修饰的 get() 方法和注册到 list 的方法和往常一样都有一个初始的 self 参数.

测试Patchwork 类:

<预><代码>>>>pw = 拼布(a=1, b=2, c=3)>>>pw.get("b")2>>>pw.get(["a", "c"])[1, 3]

Python 3.4 added the ability to define function overloading with static methods. This is essentially the example from the documentation:

from functools import singledispatch


class TestClass(object):
    @singledispatch
    def test_method(arg, verbose=False):
        if verbose:
            print("Let me just say,", end=" ")

        print(arg)

    @test_method.register(int)
    def _(arg):
        print("Strength in numbers, eh?", end=" ")
        print(arg)

    @test_method.register(list)
    def _(arg):
        print("Enumerate this:")

        for i, elem in enumerate(arg):
            print(i, elem)

if __name__ == '__main__':
    TestClass.test_method(55555)
    TestClass.test_method([33, 22, 11])

In its purest form, the singledispatch implementation relies on the first argument to identify type, therefore making it tricky to extend this functionality to instance methods.

Does anyone have any advice for how to use (or jerry-rig) this functionality to get it to work with instance methods?

解决方案

Update: As of Python 3.8, functools.singledispatchmethod allows single dispatch on methods, classmethods, abstractmethods, and staticmethods.

For older Python versions, see the rest of this answer.

Looking at the source for singledispatch, we can see that the decorator returns a function wrapper(), which selects a function to call from those registered based on the type of args[0] ...

    def wrapper(*args, **kw):
        return dispatch(args[0].__class__)(*args, **kw)

... which is fine for a regular function, but not much use for an instance method, whose first argument is always going to be self.

We can, however, write a new decorator methdispatch, which relies on singledispatch to do the heavy lifting, but instead returns a wrapper function that selects which registered function to call based on the type of args[1]:

from functools import singledispatch, update_wrapper

def methdispatch(func):
    dispatcher = singledispatch(func)
    def wrapper(*args, **kw):
        return dispatcher.dispatch(args[1].__class__)(*args, **kw)
    wrapper.register = dispatcher.register
    update_wrapper(wrapper, func)
    return wrapper

Here's a simple example of the decorator in use:

class Patchwork(object):

    def __init__(self, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    @methdispatch
    def get(self, arg):
        return getattr(self, arg, None)

    @get.register(list)
    def _(self, arg):
        return [self.get(x) for x in arg]

Notice that both the decorated get() method and the method registered to list have an initial self argument as usual.

Testing the Patchwork class:

>>> pw = Patchwork(a=1, b=2, c=3)
>>> pw.get("b")
2
>>> pw.get(["a", "c"])
[1, 3]

这篇关于如何将 functools.singledispatch 与实例方法一起使用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆