我有一个像类这样的字典,用于存储一些值作为属性。我最近添加了一些逻辑(__getattr__
),如果属性不存在则返回None。一旦我做了这个泡菜崩溃,我想要了解为什么?
测试代码:
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
我希望它返回无。
答案 0 :(得分:13)
实现__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)
我通常会通过以下划线开头的所有名称,以便我可以回避内部符号的魔力。
答案 1 :(得分:5)
当您的班级中没有属性时,您需要提出AttributeError
:
def __getattr__(self, key):
i = self.__getitem__(key)
if i == None:
raise AttributeError
return self.__getitem__(key)
我将假设此行为是必需的。来自getattr的python文档,“当一个属性查找没有找到通常位置的属性时调用(即它不是一个实例属性,也不是在类树中找到自己).name是属性name。此方法应返回(计算的)属性值或引发AttributeError异常。“
除非你引发异常,否则无法告诉pickle等找不到它正在寻找的属性。例如,在你的错误消息中,pickle正在寻找一个名为__getnewargs__
的特殊可调用方法,pickle期望如果找不到AttributeError异常,则返回值是可调用的。
我想你周围的一个潜在的工作可能会尝试定义pickle正在寻找的所有特殊方法作为虚拟方法吗?