gpt4 book ai didi

python - 应用于方法的可调用对象装饰器不会在输入中获取自参数

转载 作者:太空狗 更新时间:2023-10-30 00:55:59 25 4
gpt4 key购买 nike

import functools


class Decor(object):
def __init__(self, func):
self.func = func

def __call__(self, *args, **kwargs):
def closure(*args, **kwargs):
print args, kwargs
return self.func(*args, **kwargs)
return closure(*args, **kwargs)


class Victim(object):
@Decor
def sum(self, a, b):
return a+b


v = Victim()
v.sum(1, 2)

结果:

(1, 2) {}
Traceback (most recent call last):
File "test.py", line 19, in <module>
v.sum(1, 2)
File "test.py", line 11, in __call__
return closure(*args, **kwargs)
File "test.py", line 10, in closure
return self.func(*args, **kwargs)
TypeError: sum() takes exactly 3 arguments (2 given)

如何获取方法的 self 参数?

更新:我设法对 Martijn 的答案进行了更有用的改编,它返回 Decor 对象以响应 __get__,但同时绑定(bind) self 参数,当它作为对象的方法调用时。使用此版本,您可以说例如Victim.sum.hooks.append(my_favorite_function)my_favorite_function 将在 Victim.sum 之前调用。 警告:此版本线程不安全

class Decor(object):
def __init__(self, func):
self.func = func
self.hooks = []
wraps(self.func)(self)

def __get__(self, instance, klass):
if instance != None: self.instance = instance
if klass != None: self.klass = klass
return self

def __call__(self, *args, **kwargs):
def closure(*args, **kwargs):
for function in self.hooks:
function(*args, **kwargs)
func = self.func
retval = func(*args, **kwargs) #kwargs_copy #called with notify = False
return retval
return closure.__get__(self.instance, self.klass)(*args, **kwargs)

最佳答案

Python 函数充当 descriptors ,这意味着每当您访问类或实例上的函数时,它们的 .__get__() method被调用并返回一个方法对象,该对象保留对原始函数的引用,对于实例,还保留对实例的引用。然后方法对象充当包装器;当被调用时,它们调用底层函数并将实例引用作为 self 传递。

另一方面,您的可调用类对象没有实现描述符协议(protocol),它没有.__get__() 方法,因此永远没有机会绑定(bind)到实例。您必须自己实现此功能:

class Decor(object):
def __init__(self, func):
self.func = func

def __get__(self, instance, owner):
if instance is None:
return self
d = self
# use a lambda to produce a bound method
mfactory = lambda self, *args, **kw: d(self, *args, **kw)
mfactory.__name__ = self.func.__name__
return mfactory.__get__(instance, owner)

def __call__(self, instance, *args, **kwargs):
def closure(*args, **kwargs):
print instance, args, kwargs
return self.func(instance, *args, **kwargs)
return closure(*args, **kwargs)

演示:

>>> class Victim(object):
... @Decor
... def sum(self, a, b):
... return a+b
...
>>> v = Victim()
>>> v.sum
<bound method Victim.sum of <__main__.Victim object at 0x11013d850>>
>>> v.sum(1, 2)
<__main__.Victim object at 0x11013d850> (1, 2) {}
3

将绑定(bind)的实例直接存储在 Decor 实例上不是一个好主意;这是一个类属性,在实例之间共享。设置 self.instance 既不是线程安全的,也不允许存储方法供以后调用;最近的 __get__ 调用将改变 self.instance 并导致难以解决的错误。

您始终可以返回自定义代理对象而不是方法:

class DecorMethod(object):
def __init__(self, decor, instance):
self.decor = decor
self.instance = instance

def __call__(self, *args, **kw):
return self.decor(instance, *args, **kw)

def __getattr__(self, name):
return getattr(self.decor, name)

def __repr__(self):
return '<bound method {} of {}>'.format(self.decor, type(self))

并在您的 Decor.__get__ 中使用它,而不是生成一个方法:

def __get__(self, instance, owner):
if instance is None:
return self
return DecorMethod(self, instance)

此处的DecorMethod 将对未知属性的任何请求传递回Decor 装饰器实例:

>>> class Victim(object):
... @Decor
... def sum(self, a, b):
... return a + b
...
>>> v = Victim()
>>> v.sum
<bound method <__main__.Decor object at 0x102295390> of <class '__main__.DecorMethod'>>
>>> v.sum.func
<function sum at 0x102291848>

关于python - 应用于方法的可调用对象装饰器不会在输入中获取自参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22545339/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com