通过装饰师';将函数转换为pythonrq

通过装饰师';将函数转换为pythonrq,python,python-3.x,decorator,Python,Python 3.x,Decorator,如何将装饰师的功能传递到工作中 我有一个decorator,可以使用这个函数运行一个作业 @job def queueFunction(passedFunction, *args, **kwargs): # Do some stuff passedFunction(*args, **kwargs) def myDecorator(async=True): def wrapper(function): def wrappedFunc(*args, **kw

如何将装饰师的功能传递到工作中

我有一个decorator,可以使用这个函数运行一个作业

@job
def queueFunction(passedFunction, *args, **kwargs):
    # Do some stuff
    passedFunction(*args, **kwargs)

def myDecorator(async=True):
    def wrapper(function):
        def wrappedFunc(*args, **kwargs):
            data = DEFAULT_DATA
            if async:
                queueFunction.delay(function, *args, **kwargs)
            else:
                data = queueFunction(function, *args, **kwargs)
            return data
        return wrappedFunc
    return wrapper
我尝试使用它时出错

Can't pickle <function Model.passedFunction at 0x7f410ad4a048>: it's not the same object as modelInstance.models.Model.passedFunction
无法pickle:它与modelInstance.models.Model.passedFunction不是同一个对象

使用Python3.4时,发生的情况是将原始函数(或方法)传递给
queueFunction.delay()
函数,但该函数与其限定名所说的函数不同

为了在worker中运行函数,pythonrq使用序列化函数及其参数。但是函数(和类)被序列化为可导入的名称,当反序列化
pickle
模块时,只导入记录的名称。但它首先会检查是否会生成正确的对象。因此,在酸洗时,将对限定名称进行测试,以再次检查它是否会生成完全相同的对象

如果我们使用
pickle.loads
作为示例函数,那么大致情况如下:

>>> import pickle
>>> import sys
>>> sample_function = pickle.loads
>>> module_name = sample_function.__module__
>>> function_name = sample_function.__qualname__
>>> recorded_name = f"{module_name}.{function_name}"
>>> recorded_name
'_pickle.loads'
>>> parent, obj = sys.modules[module_name], None
>>> for name in function_name.split("."):  # traverse a dotted path of names
...     obj = getattr(parent, name)
...
>>> obj is sample_function
True
注意,
pickle.loads
实际上是
\u pickle.loads
;这并不重要,但重要的是可以访问
\u pickle
,并且它有一个可以通过使用限定名找到的对象,而它仍然是同一个对象。这甚至适用于类上的方法(
modulename.ClassName.method\u name

但是,当您装饰一个函数时,您可能正在替换该函数对象:

>>> def decorator(f):
...     def wrapper(*args, **kwargs):
...         return f, f(*args, **kwargs)
...     return wrapper
...
>>> @decorator
... def foo(): pass
...
>>> foo.__qualname__
'decorator.<locals>.wrapper'
>>> foo()[0].__qualname__  # original function
'foo'
@wrapps(function)
装饰器确保
wrappedFunc.\uuuu qualname\uuuu
设置为
function
,因此如果
function
被命名为
foo
,那么现在就是
wrappedFunc
函数对象。
wrappedFunc.original.\uuu qualname\uu+=“.original”
语句然后将
wrappedFunc.original
的限定名设置为
foo.original
,这正是
pickle
可以再次找到它的地方

注意:我将
async
重命名为
async
,以使上述代码在Python 3.7及更高版本上工作;从Python3.7开始

我还看到您正在决定在装修时运行同步或异步的东西。在这种情况下,我会重新编写它,以避免每次调用函数时都检查
aync\uu
布尔标志。只需返回不同的包装:

from functools import wraps

def myDecorator(async_=True):
    def decorator(function):
        if async_:
            @wraps(function)
            def wrapper(*args, **kwargs):
                queueFunction.delay(wrappedFunc.original, *args, **kwargs)
                return DEFAULT_DATA

            # make the original available to the pickle module as "<name>.original"
            wrapper.original = function
            wrapper.original.__qualname__ += ".original"
        else:
            @wraps(function)
            def wrapper(*args, **kwargs):
                return queueFunction(function, *args, **kwargs)

        return wrapper
    return decorator

警告:在Python 3.7或更新版本中,不能将
async
用作变量名。您可能想用
async\uu
或其他名称替换该名称。@Kareem,为什么当前答案不被接受?
from functools import wraps

def myDecorator(async_=True):
    def decorator(function):
        if async_:
            @wraps(function)
            def wrapper(*args, **kwargs):
                queueFunction.delay(wrappedFunc.original, *args, **kwargs)
                return DEFAULT_DATA

            # make the original available to the pickle module as "<name>.original"
            wrapper.original = function
            wrapper.original.__qualname__ += ".original"
        else:
            @wraps(function)
            def wrapper(*args, **kwargs):
                return queueFunction(function, *args, **kwargs)

        return wrapper
    return decorator
>>> import pickle
>>> @myDecorator(True)
... def foo(): pass
...
>>> foo.original
<function foo.original at 0x10195dd90>
>>> pickle.dumps(foo.original, pickle.HIGHEST_PROTOCOL)
b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x0cfoo.original\x94\x93\x94.'