hal*_*fak 4 python pickle python-decorators
当我使用自定义可调用类作为包装器时,我正在努力挑选包装函数.
我有一个可调用的类"Dependee",它使用成员变量"depends_on"跟踪包装函数的依赖关系.我想使用装饰器来包装函数,并且还能够修改生成的包装函数.
所以我定义了我的dependee类.注意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)
...
Run Code Online (Sandbox Code Playgroud)
然后我定义我的装饰器,它将返回Dependee包装类的一个实例.
>>> class depends:
...
... def __init__(self, on=None):
... self.depends_on = on or []
...
... def __call__(self, func):
... return Dependee(func, self.depends_on)
...
Run Code Online (Sandbox Code Playgroud)
这是一个包装函数的示例.
>>> @depends(on=["foo", "bar"])
... def sum(x, y): return x+y
...
Run Code Online (Sandbox Code Playgroud)
成员变量似乎是可访问的.
>>> print(sum.depends_on)
['foo', 'bar']
Run Code Online (Sandbox Code Playgroud)
我可以按预期调用该函数.
>>> print(sum(1,2))
3
Run Code Online (Sandbox Code Playgroud)
但我不能腌制包裹的实例.
>>> 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
Run Code Online (Sandbox Code Playgroud)
我错过了什么?如何为pickle提供更合适的限定名称,以便它可以找到实例而不是原始函数.请注意,手动包装工作正常.
>>> 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']
Run Code Online (Sandbox Code Playgroud)
是的,众所周知的pickle问题 - 不能腌制不能仅通过模块中的名称检索的函数或类。参见例如https://code.google.com/p/modwsgi/wiki/IssuesWithPickleModule以获得清晰的示例(特别是关于这如何影响modwsgi,但也包括一般问题)。
在这种情况下,由于您所做的只是向函数添加属性,您可以使用一种简化的方法:
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
Run Code Online (Sandbox Code Playgroud)
这return func是关键思想——返回被装饰的同一个对象(可能在装饰它之后,如这里,具有附加属性——但是,不是不同的对象,否则名称与身份问题会出现)。
现在这将起作用(只是您的原始代码,仅depends按上述方式更改):
$ python d.py
['foo', 'bar']
3
c__main__
sum
p0
.
Run Code Online (Sandbox Code Playgroud)
当然,这不是一个通用的解决方案(它只有在装饰器返回它正在装饰的同一个对象可行的情况下才有效),只是一个适用于您的示例的解决方案。
我不知道任何能够在没有此限制的情况下序列化和反序列化 Python 对象的序列化方法,唉。
你需要一个更好的序列化器,比如dill.至于它是如何工作的,dill只需要注册大量的python类型copy_reg- 它也__main__类似于模块,最后可以通过引用或按对象进行序列化.因此,如果要序列化函数或类,最后一位是相关的,并使用pickle获取类/函数定义.与参考序列化相比,它比泡菜更大一些,但它更强大.
这是你的代码:
>>> 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']
>>>
Run Code Online (Sandbox Code Playgroud)
获取dill此:https://github.com/uqfoundation