装饰方法(类方法重载)

时间:2011-05-15 15:32:53

标签: python methods python-3.x decorator

受到What are good uses for Python3's "Function Annotations"中穆罕默德·阿尔卡鲁里回答的启发,我希望为方法而不是常规函数执行此操作multimethod。但是,当我这样做时

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}
    def __call__(self, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)
    if function is None:
        raise TypeError("no match")
    return function(*args)
def register(self, types, function):
    if types in self.typemap:
        raise TypeError("duplicate registration")
    self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)
    types = tuple(function.__annotations__.values())
    mm.register(types, function)
    return mm

class A:
@multimethod
def foo(self, a: int):
    return "an int"

a = A() 
print( a.foo( 1 ) ) 

我明白了:

Traceback (most recent call last):
  File "test.py", line 33, in <module>
    print( a.foo( 1 ) )
  File "test.py", line 12, in __call__
    return function(*args)
TypeError: foo() takes exactly 2 arguments (1 given)

由于self参数,正如Decorating a method中所解释的那样,这似乎是预期的。

但我不知道如何让它发挥作用。好吧,当我删除“自我”时,它工作(几乎)很好,但我不想删除它。请注意,我正在练习这个,我知道有一些libs,提供方法重载。

我尝试了什么:

  • 非常愚蠢,但想尝试 - 在self中添加参数def multimethod( function ) - 同样的错误

  • 我考虑添加__init__ class MultiMethod第三个参数 - obj并存储self作为成员,但我无法通过multimethod因为它是一种功能。

  • 我不想为装饰器添加参数,所以忽略了这些选项(如果可能的话)

我读过几个类似的问题,但没找到我要找的东西。我很确定这是一个虚假的问题,但我没有想法。

1 个答案:

答案 0 :(得分:5)

您遇到的基本问题是您使用类代替函数。没有机制将该类绑定到它调用的实例,这与自动发生的函数不同。

简而言之,当您执行a.foo( .. )时,它会返回MultiMethod,但此对象不知道它应该绑定到a

您必须以某种方式传递实例。一种简单的方法是将它全部包装在一个函数中,让Python做到这一点:

registry = {}

class MultiMethod(object):
    def __init__(self, name):
        self.name = name
        self.typemap = {}

    # self = a MultiMethod instance, instance = the object we want to bind to
    def __call__(self, instance, *args):
        types = tuple(arg.__class__ for arg in args) # a generator expression!
        function = self.typemap.get(types)

        if function is None:
            raise TypeError("no match")
        return function(instance, *args)

    def register(self, types, function):
        if types in self.typemap:
            raise TypeError("duplicate registration")
        self.typemap[types] = function

def multimethod(function):
    name = function.__name__
    mm = registry.get(name)
    if mm is None:
        mm = registry[name] = MultiMethod(name)

    types = tuple(function.__annotations__.values())
    mm.register(types, function)
    # return a function instead of a object - Python binds this automatically
    def getter(instance, *args, **kwargs):
        return mm(instance, *args, **kwargs)
    return getter

class A:
    @multimethod
    def foo(self, a: int):
        return "an int", a

a = A() 
print( a.foo( 1 ) )

更复杂的方法是在执行此绑定的A类上编写自己的描述符。