简体   繁体   English

装饰和包装的函数没有将 self 传递给实例方法

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM