酸洗装饰可调用类包装器 [英] Pickling decorated callable class wrapper
问题描述
当我使用自定义的可调用类作为包装器时,我正在努力使包装的函数腌制.
I'm struggling to pickle a wrapped function when I use a custom callable class as a wrapper.
我有一个可调用类"Dependee",该类跟踪成员变量为"depends_on"的包装函数的依赖关系.我想使用装饰器来包装函数,并且还可以对所得的包装函数进行腌制.
I have a callable class "Dependee" that keeps track of dependencies for a wrapped function with a member variable "depends_on". I'd like to use a decorator to wrap functions and also be able to pickle the resulting wrapped function.
因此,我定义了我的受养人课程.请注意functools.update_wrapper的使用.
So I define my dependee class. Note the use of functools.update_wrapper.
>>> class Dependee:
...
... def __init__(self, func, depends_on=None):
... self.func = func
... self.depends_on = depends_on or []
... functools.update_wrapper(self, func)
...
... def __call__(self, *args, **kwargs):
... return self.func(*args, **kwargs)
...
然后,我定义我的装饰器,使其返回Dependee包装器类的实例.
Then I define my decorator such that it will return an instance of the Dependee wrapper class.
>>> class depends:
...
... def __init__(self, on=None):
... self.depends_on = on or []
...
... def __call__(self, func):
... return Dependee(func, self.depends_on)
...
这是一个包装函数的示例.
Here's an example of a wrapped function.
>>> @depends(on=["foo", "bar"])
... def sum(x, y): return x+y
...
该成员变量似乎可以访问.
The member variable seems to be accessible.
>>> print(sum.depends_on)
['foo', 'bar']
我可以按预期调用该函数.
I can call the function as expected.
>>> print(sum(1,2))
3
但是我不能腌制包装好的实例.
But I can't pickle the wrapped instance.
>>> print(pickle.dumps(sum))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <function sum at 0x7f543863fbf8>: it's not the same object as __main__.sum
我想念什么?我如何给pickle一个更合适的限定名称,以便它可以找到实例而不是原始函数.请注意,手动换行效果很好.
What am I missing? How can I give pickle a more appropriately qualified name so that it can find the instance rather than the original function. Note that manual wrapping works just fine.
>>> def sum2_func(x,y): return x+y
...
>>> sum2 = Dependee(sum2_func, depends_on=["foo", "bar"])
>>> print(sum2.depends_on)
['foo', 'bar']
>>> print(sum2(1,2))
3
>>> print(pickle.loads(pickle.dumps(sum2)).depends_on)
['foo', 'bar']
推荐答案
是的,众所周知的pickle
问题-不能腌制不能仅通过模块中的名称检索的函数或类.参见例如 https://code.google.com/p/modwsgi/wiki/IssuesWithPickleModule提供清晰的示例(特别是有关这如何影响modwsgi
的问题,以及一般情况下的问题).
Yep, well-known pickle
problem -- can't pickle functions or classes that can't just be retrieved by their name in the module. See e.g https://code.google.com/p/modwsgi/wiki/IssuesWithPickleModule for clear examples (specifically on how this affects modwsgi
, but also of the issue in general).
在这种情况下,由于您要做的只是向函数添加属性,因此可以采用一种简化的方法:
In this case since all you're doing is adding attributes to the function, you can get away with a simplified approach:
class depends:
def __init__(self, on=None):
self.depends_on = on or []
def __call__(self, func):
func.func = func
func.depends_on = self.depends_on or []
return func
return func
是关键思想-返回正在装饰的相同对象(可能是在装饰之后,如此处所示,具有其他属性),但不是一个不同的对象,否则名称-vs-identity问题就会出现.)
the return func
is the key idea -- return the same object that's being decorated (possibly after decorating it, as here, with additional attributes -- but, not a different object, else the name-vs-identity issue perks up).
现在这将起作用(只是您的原始代码,只需如上所述更改depends
即可):
Now this will work (just your original code, only changing depends
as above):
$ python d.py
['foo', 'bar']
3
c__main__
sum
p0
.
当然,这不是通用解决方案(仅在装饰器返回与其装饰相同的对象可行的情况下才起作用),只是在您的示例中有效.
Of course, this isn't a general-purpose solution (it only works if it's feasible for the decorator to return the same object it's decorating), just one that works in your example.
我不知道有没有什么序列化方法可以在没有此限制的情况下对Python对象进行序列化和反序列化.
I am not aware of any serialization approach able to serialize and de-serialize Python objects without this limitation, alas.
这篇关于酸洗装饰可调用类包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!