【问题标题】:Decorating method (class methods overloading)装饰方法(类方法重载)
【发布时间】:2011-08-25 22:41:02
【问题描述】:

What are good uses for Python3's "Function Annotations" 中的 Muhammad Alkarouri 回答的启发,我想为方法而不是常规函数执行此 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)

这似乎是意料之中的,正如 Decorating a method 中所解释的那样,因为 self 参数。

但我不知道如何使它工作。好吧,当我删除“自我”时,它(几乎)工作正常,但我不想删除它。请注意,我这样做是为了练习,我知道有一些库提供方法重载。

我尝试了什么:

  • 很傻,但想试试 - 在def multimethod( function ) 中添加参数self - 同样的错误

  • 我曾考虑在class MultiMethod__init__ 中添加第三个参数-obj 并将self 存储为成员,但我无法通过multimethod 执行此操作,因为它是一个函数.

  • 我不想为装饰器添加参数,因此忽略此选项(如果可能的话)

我阅读了几个类似的问题,但没有找到我想要的。我很确定这是一个愚蠢的问题,但我没有想法。

【问题讨论】:

    标签: python methods python-3.x decorator


    【解决方案1】:

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

    简而言之,当你执行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 类上编写您自己的描述符。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-14
      • 2020-01-11
      • 2012-02-09
      • 2013-05-23
      • 1970-01-01
      • 2014-12-07
      相关资源
      最近更新 更多