尝试在__getattr__中返回默认值时,Python pickle崩溃

时间:2011-06-20 18:51:14

标签: python python-2.7 pickle

我有一个像类这样的字典,用于存储一些值作为属性。我最近添加了一些逻辑(__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

我希望它返回无。

2 个答案:

答案 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正在寻找的所有特殊方法作为虚拟方法吗?