可应用于方法的可调用对象装饰器不会在输入上获取自变量 [英] Callable object decorator applied to method doesn't get self argument on input
问题描述
import functools
class Decor(object):
def __init __(self,func):
self。 func = func
def __call __(self,* args,** kwargs):
def closure(* args,** kwargs):
print args,kwargs
返回self.func(* args,** kwargs)
返回闭包(* args,** kwargs)
类受害者(object):
@装饰
def sum(self,a,b):
返回a + b
v =受害者()
v.sum(1,2 )
结果:
(1,2){}
Traceback(最近一次调用最后一次):
在< module>文件中的第19行test.py
v.sum(1,2)
文件test.py,第11行,在__call__
返回闭包(* args,** kwargs)
文件test。 py,第10行,关闭
返回self.func(* args,** kwargs)
TypeError:sum()接受3个参数(给出2个)
如何为该方法获取 self
参数?
更新:
我设法创建了一个更有用的Martijn的答案,它返回 Decor
对象来响应 __ get __
,但同时绑定 self
参数,当它被称为方法目的。有了这个版本,你可以说例如 Victim.sum.hooks.append(my_favorite_function)
和 my_favorite_function
将在 Victim之前被调用。总和
。 警告:此版本线程不安全。
class Decor对象):
def __init __(self,func):
self.func = func
self.hooks = []
wrap(self.func)(self)
def __get __(self,instance,klass):
if instance!= None:self.instance = instance
如果klass!= None:self.klass = klass
return self
def __call __(self,* args,** kwargs):
def closure(* args,** kwargs):
for self.hooks中的函数:
函数(* args,** kwargs)
func = self.func
retval = func(* args,** kwargs)#kwargs_copy #calcify notify = False
return retval
return closure .__ get __(self.instance,self.klass)(* args,** kwargs)
Python函数充当描述符,这意味着只要你访问一个类或实例的函数,它们的 .__ get __()
方法被调用,并返回一个方法对象,该方法对象保留对原始函数的引用,并且对实例引用实例。方法对象然后作为包装器;当调用它们时,它们调用底层函数并将实例引用作为 self
传递。您的可调用类对象on另一方面,它没有实现描述符协议,它没有 .__ get __()
方法,因此永远不会有机会绑定到实例。你必须自己实现这个功能:
class Decor(object):
def __init __(self,func ):
self.func = func
def __get __(self,instance,owner):
如果实例是None:
return self
d = self
#使用lambda生成绑定方法
mfactory = lambda self,* args,** kw:d(self,* args,** kw)
mfactory .__ name__ = self。 func .__ name__
return mfactory .__ get __(instance,owner)
$ b def __call __(self,instance,* args,** kwargs):
def closure(* args,* * kwargs):
打印实例,args,kwargs
返回self.func(实例,* args,** kwargs)
返回闭包(* args,** kwargs)
演示:
>>>班级受害者(对象):
... @Decor
... def sum(self,a,b):
... return a + b
...
>>> v = Victim()
>>> v.sum
< __main __的受害者对象的绑定方法Victim.sum。0x11013d850处的受害者对象>>
>>> v.sum(1,2)
<__ main __。0x11013d850处的受害者对象> (1,2){}
3
将你绑定的实例直接存储在 Decor
实例中一个好主意;这是一个类属性,在实例之间共享。设置 self.instance
既不是线程安全的,也不允许存储方法以供以后调用;最近的 __ get __
调用会改变 self.instance
并导致难以解决的错误。
您可以始终返回自定义代理对象而不是方法:
class DecorMethod (object):
def __init __(self,decor,instance):
self.decor = decor
self.instance = instance
$ b $ def __call __(self,* args,** kw):
return self.decor(instance,* args,** kw)
$ b $ def __getattr __(self,name):
return getattr(self。装饰,名称)
def __repr __(self):
return'< bound method {}>'.format(self.decor,type(self))
并在您的 Decor中使用它.__ get __
,而不是生成一个方法:
def __get __(self,instance,owner):
如果实例是None:
返回self
DecorMethod(self,instance)
DecorMethod
在这里将所有未知属性的请求传回给 Decor
装饰器实例:
>>>班级受害者(对象):
... @Decor
... def sum(self,a,b):
... return a + b
...
>>> v = Victim()
>>> v.sum
>>> v.sum.func
<函数和在0x102291848>
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)
Results in:
(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)
How do I get self
argument for the method?
UPDATE:
I've managed to create a more useful adaptation of Martijn's answer, which returns Decor
object in response to __get__
, but at the same time binds self
argument, when it is called as a method of object. With this version you can say e.g. Victim.sum.hooks.append(my_favorite_function)
and my_favorite_function
will be called before Victim.sum
. WARNING: this version is thread-unsafe.
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 functions act as descriptors, which means that whenever you access a function on a class or instance, their .__get__()
method is invoked and a method object is returned which keeps a reference to the original function, and for instances, a reference to the instance. Method object then acts as wrappers; when called they call the underlying function and pass in the instance reference as self
.
Your callable class object, on the other hand, does not implement the descriptor protocol, it has no .__get__()
method, and thus it never is given an opportunity to bind to the instance. You'll have to implement this functionality yourself:
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)
Demo:
>>> 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
It is not a good idea to store the instance you are bound to directly on the Decor
instance; this is a class attribute, shared among instances. Setting self.instance
is neither thread-safe nor allows methods to be stored for later invocation; the most recent __get__
call will alter self.instance
and lead to hard-to-resolve bugs.
You can always return a custom proxy object instead of a method:
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))
and use that in your Decor.__get__
instead of producing a method:
def __get__(self, instance, owner):
if instance is None:
return self
return DecorMethod(self, instance)
The DecorMethod
here passes any requests for unknown attributes back to the Decor
decorator instance:
>>> 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>
这篇关于可应用于方法的可调用对象装饰器不会在输入上获取自变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!