Pickling decorated callable class wrapper

有些话、适合烂在心里 提交于 2019-12-04 11:37:13

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

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).

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.

I am not aware of any serialization approach able to serialize and de-serialize Python objects without this limitation, alas.

You just need a better serializer, like dill. As for how it works, dill just does a lot of registering python types with the equivalent of copy_reg -- it also treats __main__ similarly to a module, and lastly can serialize by reference or by object. So the last bit is relevant if you want to serialize a function or class, and take the class/function definition with the pickle. It's a little bigger of a pickle than serializing by reference, but it's more robust.

Here's your code exactly:

>>> import dill
>>> import functors
>>> 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)
... 
>>>       
>>> class depends:
...   def __init__(self, on=None):
...     self.depends_on = on or []
...   def __call__(self, func):
...     return Dependee(func, self.depends_on)
... 
>>> @depends(on=['foo','bar'])
... def sum(x,y): return x+y
... 
>>> print(sum.depends_on)
['foo', 'bar']
>>> print(sum(1,2))
3
>>> _sum = dill.dumps(sum)
>>> sum_ = dill.loads(_sum)
>>> print(sum_(1,2))
3
>>> print(sum_.depends_on)
['foo', 'bar']
>>> 

Get dill here: https://github.com/uqfoundation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!