Python pickle在__getattr__中返回默认值时崩溃
我有一个像字典一样的类,用来存储一些值作为属性。最近我添加了一些逻辑(__getattr__
),让它在属性不存在时返回 None。可是一这样做,pickle 就崩溃了,我想知道这是为什么?
测试代码:
import cPickle
class DictionaryLike(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __iter__(self):
return iter(self.__dict__)
def __getitem__(self, key):
if(self.__dict__.has_key(key)):
return self.__dict__[key]
else:
return None
''' This is the culprit...'''
def __getattr__(self, key):
print 'Retreiving Value ' , key
return self.__getitem__(key)
class SomeClass(object):
def __init__(self, kwargs={}):
self.args = DictionaryLike(**kwargs)
someClass = SomeClass()
content = cPickle.dumps(someClass,-1)
print content
结果:
Retreiving Value __getnewargs__
Traceback (most recent call last):
File <<file>> line 29, in <module>
content = cPickle.dumps(someClass,-1)
TypeError: 'NoneType' object is not callable`
我是不是做了什么傻事?我看到有帖子说,deepcopy() 可能需要我在键不存在时抛出一个异常?如果是这样,有没有简单的方法可以实现我想要的,而不需要抛出异常呢?
最终结果是,如果有些调用
someClass.args.i_dont_exist
我希望它返回 None。
2 个回答
当你的类里没有某个属性时,你需要抛出一个 AttributeError
错误:
def __getattr__(self, key):
i = self.__getitem__(key)
if i == None:
raise AttributeError
return self.__getitem__(key)
我假设这是必须的行为。根据 Python 的文档,getattr 的说明,“当在常规地方找不到某个属性时会被调用(也就是说,它既不是实例属性,也不在类的继承树中)。name 是属性的名称。这个方法应该返回计算出的属性值,或者抛出一个 AttributeError 异常。”
如果你不抛出这个异常,就没有办法告诉 pickle 等工具它要找的属性不存在。例如,在你的错误信息中,pickle 正在寻找一个叫 __getnewargs__
的特殊可调用方法,pickle 期望如果没有找到 AttributeError 异常,那么返回值是可以调用的。
我想一个可能的解决办法是,你可以尝试把 pickle 要找的所有特殊方法定义为虚拟方法?
实现 __getattr__
有点复杂,因为它会在每次访问不存在的属性时被调用。在你的情况下,pickle
模块会检查你的类是否有一个特殊的方法 __getnewargs__
,但是它得到了 None
,显然这个东西是不能被调用的。
你可能想要修改 __getattr__
,让它在处理一些特殊名称时调用基类的实现:
def __getattr__(self, key):
if key.startswith('__') and key.endswith('__'):
return super(DictionaryLike, self).__getattr__(key)
return self.__getitem__(key)
我通常会跳过所有以下划线开头的名称,这样我就可以避免处理内部符号的特殊情况。