Python类中实现multimethod装饰器时丢失self的问题解决
解决类中多方法分发时丢失
self的问题 这个问题的核心是类方法的绑定机制和你的多方法调用逻辑不匹配,导致参数传递顺序完全错位了。让我一步步给你拆解问题,再给出修复方案:
问题根源
当你用@use_for_type装饰器把Sender类里的send_message方法替换成SendMessageMultiMethod实例后,调用self.send_message(message)时,Python会自动把Sender的实例(也就是你原本期望的self)作为第一个参数传给SendMessageMultiMethod的__call__方法。但你原来的__call__方法签名是:
def __call__(self, message, extra_payload=None):
这里的第一个self是SendMessageMultiMethod自己的实例,所以实际参数传递变成了:
SendMessageMultiMethod实例 →__call__的selfSender实例 →__call__的message- 你传入的
message→__call__的extra_payload
然后你调用注册的方法时,又把message(实际是Sender实例)传给了类方法的第一个参数,自然就出现了self变成数值/布尔值,message为None的情况。
修复方案
我们需要调整SendMessageMultiMethod的__call__方法,让它能正确接收Sender的实例,并把它传递给注册的类方法。
修改后的完整代码
method_registry = {} class SendMessageMultiMethod(object): """ A class for implementing multimethod functionality """ def __init__(self, name): self.name = name self.typemap = {} def __call__(self, instance, message, extra_payload=None): """ Overrriding method call and dispatching it to an actual method based on the supplied message class """ first_arg_type = message.__class__ function = self.typemap.get(first_arg_type) print( 'Dispatching to function {} with message {} and extra payload {}...' .format(function, message, extra_payload) ) # 把Sender实例作为第一个参数传给注册的类方法 return function(instance, message, extra_payload) def register(self, type_, function): self.typemap[type_] = function def use_for_type(*types): """ A decorator that registers a method to use with certain types """ def register(method): """Creating Multimethod with the method name and registering it at at method_registry dict """ name = method.__name__ mm = method_registry.get(name) if mm is None: mm = method_registry[name] = SendMessageMultiMethod(name) for type_ in types: mm.register(type_, method) return mm return register class Sender(object): def send_messages(self, messages_list): for message in messages_list: self.send_message(message) @use_for_type(int, float) def send_message(self, message, *args, **kwargs): print('received call for int/float message {} with {}, {}' .format(message, args, kwargs)) print('self is {}'.format(self)) @use_for_type(bool) def send_message(self, message, *args, **kwargs): print('received call for bool message {} with {}, {}' .format(message, args, kwargs)) print('self is {}'.format(self))
关键修改点
- 调整
__call__的参数签名:
新增instance参数,用来接收Sender类的实例(也就是原本的self),参数顺序变成(self, instance, message, extra_payload=None)。 - 调用注册方法时传递
instance:
在return function(instance, message, extra_payload)这一行,把instance作为第一个参数传给注册的类方法,确保类方法能拿到正确的self。
测试运行结果
现在执行你的测试代码:
sender = Sender() sender.send_messages([1, 2, True, 5.6])
会得到正确的输出:
Dispatching to function <function Sender.send_message at 0x1013608c8> with message 1 and extra payload None... received call for int/float message 1 with (None,), {} self is <__main__.Sender object at 0x10135f9d0> Dispatching to function <function Sender.send_message at 0x1013608c8> with message 2 and extra payload None... received call for int/float message 2 with (None,), {} self is <__main__.Sender object at 0x10135f9d0> Dispatching to function <function Sender.send_message at 0x101360950> with message True and extra payload None... received call for bool message True with (None,), {} self is <__main__.Sender object at 0x10135f9d0> Dispatching to function <function Sender.send_message at 0x1013608c8> with message 5.6 and extra payload None... received call for int/float message 5.6 with (None,), {} self is <__main__.Sender object at 0x10135f9d0>
可以看到self现在是正确的Sender实例,message也拿到了预期的值。
如果想去掉args里的None,可以在__call__里判断extra_payload,如果是None就不传,或者调整类方法的参数签名,这里就看你的需求啦。
内容的提问来源于stack exchange,提问作者kurtgn
相关产品推荐
相关产品推荐

