通过装饰师';将函数转换为pythonrq
如何将装饰师的功能传递到工作中 我有一个decorator,可以使用这个函数运行一个作业通过装饰师';将函数转换为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
@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.'