为什么 python 的 pickle 没有将方法序列化为默认参数?
why python's pickle is not serializing a method as default argument?
我正在尝试使用 pickle 在 2 个服务器之间通过网络传输 python 对象。我创建了一个简单的 class,subclasses dict
并且我正在尝试使用 pickle 进行编组:
def value_is_not_none(value):
return value is not None
class CustomDict(dict):
def __init__(self, cond=lambda x: x is not None):
super().__init__()
self.cond = cond
def __setitem__(self, key, value):
if self.cond(value):
dict.__setitem__(self, key, value)
我首先尝试使用 pickle
进行编组,但是当我取消编组时,我收到了与 lambda
表达式相关的错误。
然后我尝试用 dill
进行编组,但似乎没有调用 __init__
。
然后我再次尝试使用 pickle
,但是我将 value_is_not_none()
函数作为 cond
参数传递 - 同样 __init__()
似乎没有被调用并且__setitem__()
上的解编组失败(cond
是 None
)。
这是为什么?我在这里错过了什么?
如果我尝试运行以下代码:
obj = CustomDict(cond=value_is_not_none)
obj['hello'] = ['world']
payload = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
obj2 = pickle.loads(payload)
失败
AttributeError: 'CustomDict' object has no attribute 'cond'
这是一个不同于以下问题的问题:Python, cPickle, pickling lambda functions
当我尝试将 dill
与 lambda
一起使用时,它无法正常工作,我还尝试传递一个函数,但它也失败了。
pickle
正在加载您的字典数据 ,然后 它已恢复您实例上的属性。因此,当为字典键值对调用 __setitem__
时,尚未设置 self.cond
属性。
注意pickle
永远不会调用__init__
;相反,它将创建一个完全 空白的 实例并直接在该实例上恢复 __dict__
属性命名空间。
你有两个选择:
默认为cond=None
,如果仍然设置为None
则忽略条件:
class CustomDict(dict):
def __init__(self, cond=None):
super().__init__()
self.cond = cond
def __setitem__(self, key, value):
if getattr(self, 'cond', None) is None or self.cond(value):
dict.__setitem__(self, key, value)
需要getattr()
,因为空白实例根本没有cond
属性(它没有设置为None
,该属性完全缺失)。您可以将 cond = None
添加到 class:
class CustomDict(dict):
cond = None
然后只测试 if self.cond is None or self.cond(value):
.
定义自定义 __reduce__
method 来控制恢复时如何创建初始对象:
def _default_cond(v): return v is not None
class CustomDict(dict):
def __init__(self, cond=_default_cond):
super().__init__()
self.cond = cond
def __setitem__(self, key, value):
if self.cond(value):
dict.__setitem__(self, key, value)
def __reduce__(self):
return (CustomDict, (self.cond,), None, None, iter(self.items()))
__reduce__
预计 return 一个元组:
- 可以直接 pickle 的可调用函数(这里 class 可以)
- 该可调用对象的位置参数元组;在 unpickling 上调用第一个元素作为参数传入第二个元素,因此通过将其设置为
(self.cond,)
我们确保创建新实例时使用 cond
作为参数传入并且 now CustomDict.__init__()
将 调用。
- 接下来的 2 个位置用于
__setstate__
方法(此处忽略)和类列表类型,因此我们将它们设置为 None
。
- 最后一个元素是 pickle 将为我们恢复的键值对的迭代器。
请注意,我在这里也用一个函数替换了 cond
的默认值,因此您不必依赖 dill
进行酸洗。
我正在尝试使用 pickle 在 2 个服务器之间通过网络传输 python 对象。我创建了一个简单的 class,subclasses dict
并且我正在尝试使用 pickle 进行编组:
def value_is_not_none(value):
return value is not None
class CustomDict(dict):
def __init__(self, cond=lambda x: x is not None):
super().__init__()
self.cond = cond
def __setitem__(self, key, value):
if self.cond(value):
dict.__setitem__(self, key, value)
我首先尝试使用 pickle
进行编组,但是当我取消编组时,我收到了与 lambda
表达式相关的错误。
然后我尝试用 dill
进行编组,但似乎没有调用 __init__
。
然后我再次尝试使用 pickle
,但是我将 value_is_not_none()
函数作为 cond
参数传递 - 同样 __init__()
似乎没有被调用并且__setitem__()
上的解编组失败(cond
是 None
)。
这是为什么?我在这里错过了什么?
如果我尝试运行以下代码:
obj = CustomDict(cond=value_is_not_none)
obj['hello'] = ['world']
payload = pickle.dumps(obj, protocol=pickle.HIGHEST_PROTOCOL)
obj2 = pickle.loads(payload)
失败
AttributeError: 'CustomDict' object has no attribute 'cond'
这是一个不同于以下问题的问题:Python, cPickle, pickling lambda functions
当我尝试将 dill
与 lambda
一起使用时,它无法正常工作,我还尝试传递一个函数,但它也失败了。
pickle
正在加载您的字典数据 ,然后 它已恢复您实例上的属性。因此,当为字典键值对调用 __setitem__
时,尚未设置 self.cond
属性。
注意pickle
永远不会调用__init__
;相反,它将创建一个完全 空白的 实例并直接在该实例上恢复 __dict__
属性命名空间。
你有两个选择:
默认为
cond=None
,如果仍然设置为None
则忽略条件:class CustomDict(dict): def __init__(self, cond=None): super().__init__() self.cond = cond def __setitem__(self, key, value): if getattr(self, 'cond', None) is None or self.cond(value): dict.__setitem__(self, key, value)
需要
getattr()
,因为空白实例根本没有cond
属性(它没有设置为None
,该属性完全缺失)。您可以将cond = None
添加到 class:class CustomDict(dict): cond = None
然后只测试
if self.cond is None or self.cond(value):
.定义自定义
__reduce__
method 来控制恢复时如何创建初始对象:def _default_cond(v): return v is not None class CustomDict(dict): def __init__(self, cond=_default_cond): super().__init__() self.cond = cond def __setitem__(self, key, value): if self.cond(value): dict.__setitem__(self, key, value) def __reduce__(self): return (CustomDict, (self.cond,), None, None, iter(self.items()))
__reduce__
预计 return 一个元组:- 可以直接 pickle 的可调用函数(这里 class 可以)
- 该可调用对象的位置参数元组;在 unpickling 上调用第一个元素作为参数传入第二个元素,因此通过将其设置为
(self.cond,)
我们确保创建新实例时使用cond
作为参数传入并且 nowCustomDict.__init__()
将 调用。 - 接下来的 2 个位置用于
__setstate__
方法(此处忽略)和类列表类型,因此我们将它们设置为None
。 - 最后一个元素是 pickle 将为我们恢复的键值对的迭代器。
请注意,我在这里也用一个函数替换了
cond
的默认值,因此您不必依赖dill
进行酸洗。