装饰方法(类方法重载)
受到Muhammad Alkarouri在Python3的“函数注解”有什么好的用法中回答的启发,我想为方法而不是普通函数做一个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
参数。
但我不知道该怎么让它工作。好吧,当我去掉“self”时,它(几乎)可以正常工作,但我不想去掉它。请注意,我这样做是为了练习,我知道有一些库可以提供方法重载。
我尝试过的:
很傻,但我想试试 - 在
def multimethod( function )
中添加参数self
- 结果还是同样的错误我考虑在
class MultiMethod
的__init__
中添加一个第三个参数 -obj
,并把self
存储为成员,但我无法通过multimethod
做到这一点,因为它是一个函数。我不想为装饰器添加参数,所以这个选项(如果可能的话)被忽略了
我读了几个类似的问题,但没有找到我想要的答案。我很确定这是个傻问题,但我已经没有其他想法了。
1 个回答
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
类写一个自己的描述符,来实现这个绑定。