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

Nix*_*Nix 6 python pickle python-2.7

我有一个像类这样的字典,用于存储一些值作为属性.我最近添加了一些logic(__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
Run Code Online (Sandbox Code Playgroud)

结果:

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`
Run Code Online (Sandbox Code Playgroud)

我做了些蠢事吗?我读过一篇帖子,如果一个密钥不存在,deepcopy()可能会要求我抛出异常?如果是这种情况,是否有任何简单的方法来实现我想要的而不抛出异常?

最终结果是,如果有人打电话

someClass.args.i_dont_exist
Run Code Online (Sandbox Code Playgroud)

我希望它返回None.

Fer*_*yer 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)
Run Code Online (Sandbox Code Playgroud)

我通常会以下划线开头的所有名称,以便我可以回避内部符号的魔力.


GWW*_*GWW 5

您需要AttributeError在类中不存在属性时引发:

def __getattr__(self, key):
    i = self.__getitem__(key)
    if i == None:
        raise AttributeError
    return self.__getitem__(key)
Run Code Online (Sandbox Code Playgroud)

我将假设这种行为是必需的.从getattr的python文档中,"当一个属性查找没有找到通常位置的属性时调用(即它不是一个实例属性,也不是在类树中找到self).name是属性名称.这个方法应该返回(计算的)属性值或引发AttributeError异常."

除非你提出异常,否则没有办法告诉pickle等找不到它正在寻找的属性.例如,在您的错误消息中,pickle正在寻找一个名为的特殊可调用方法__getnewargs__,pickle期望如果未找到AttributeError异常,则返回值是可调用的.

我想你周围的一个潜在的工作可能会尝试定义pickle正在寻找的所有特殊方法作为虚拟方法?