[英]Decorated and wrapped function is not passing self to instance method
I am trying to run some instance methods as background threads using a decorator.我正在尝试使用装饰器将一些实例方法作为后台线程运行。 Several nested functions are chained (as found there ) to make it work:几个嵌套函数被链接起来(如在此处找到)以使其工作:
import traceback
from functools import partial
from threading import Thread
def backgroundThread(name=''):
def fnWrapper(decorated_func):
def argsWrapper(name, *inner_args, **inner_kwargs):
def exceptionWrapper(fn, *args, **kwargs):
try:
fn(*args, **kwargs)
except:
traceback.print_exc()
if not name:
name = decorated_func.__name__
th = Thread(
name=name,
target=exceptionWrapper,
args=(decorated_func, ) + inner_args,
kwargs=inner_kwargs
)
th.start()
return partial(argsWrapper, name)
return fnWrapper
class X:
@backgroundThread()
def myfun(self, *args, **kwargs):
print(args, kwargs)
print("myfun was called")
#1 / 0
x = X()
x.myfun(1, 2, foo="bar")
x.myfun()
Output/Error (on Windows, Python 3.6.6):输出/错误(在 Windows 上,Python 3.6.6):
(2,) {'foo': 'bar'}
myfun was called
Traceback (most recent call last):
File "t3.py", line 11, in exceptionWrapper
fn(*args, **kwargs)
TypeError: myfun() missing 1 required positional argument: 'self'
The code works partly, how to be able to 'bind' self
to the call: x.myfun()
which takes no arguments ?该代码部分有效,如何能够将self
'绑定'到调用: x.myfun()
不带参数?
Fundamentally, the problem is that @backgroundThread()
doesn't wrap an instance method x.myfun
;从根本上说,问题在于@backgroundThread()
没有包装实例方法x.myfun
; it wraps the function X.myfun
that is namespaced to the class.它包装了命名空间到类的函数X.myfun
。
We can inspect the wrapped result:我们可以检查包装的结果:
>>> X.myfun
functools.partial(<function backgroundThread.<locals>.fnWrapper.<locals>.argsWrapper at 0x7f0a1e2e7a60>, '')
This is not usable as a method, because functools.partial
is not a descriptor:这不能用作方法,因为functools.partial
不是描述符:
>>> X.myfun.__get__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'functools.partial' object has no attribute '__get__'
>>> class Y:
... # no wrapper
... def myfun(self, *args, **kwargs):
... print(args, kwargs)
... print("myfun was called")
... #1 / 0
...
>>> Y.myfun.__get__
<method-wrapper '__get__' of function object at 0x7f0a1e2e7940>
Because X.myfun
is not usable as a descriptor, when it is looked up via x.myfun
, it is called like an ordinary function.因为X.myfun
不能用作描述符,所以当通过x.myfun
时,它会像普通函数一样被调用。 self
does not receive the value of x
, but instead of the first argument that was passed, resulting in the wrong output for the (1, 2, foo='bar')
case and the exception for the ()
case. self
没有接收x
的值,而是接收到传递的第一个参数,从而导致(1, 2, foo='bar')
情况的错误输出和()
) 情况的异常。
Instead of having argsWrapper
accept a name
and then binding it with partial
, we can just use the name
from the closure - since we are already doing that with decorated_func
anyway.我们可以只使用闭包中的name
,而不是让argsWrapper
接受一个name
,然后将其与partial
绑定——因为无论如何我们已经在使用decorated_func
这样做了。 Thus:因此:
def backgroundThread(name=''):
def fnWrapper(decorated_func):
def argsWrapper(*inner_args, **inner_kwargs):
nonlocal name
def exceptionWrapper(fn, *args, **kwargs):
try:
fn(*args, **kwargs)
except:
traceback.print_exc()
if not name:
name = decorated_func.__name__
th = Thread(
name=name,
target=exceptionWrapper,
args=(decorated_func, ) + inner_args,
kwargs=inner_kwargs
)
th.start()
return argsWrapper
return fnWrapper
Here, nonlocal name
is needed so that argsWrapper
has access to a name from a scope that is not the immediate closure, but also isn't global.在这里,需要nonlocal name
,以便argsWrapper
可以从不是立即闭包但也不是全局的范围内访问名称。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.