【问题标题】:Python pickle crash when trying to return default value in __getattr__尝试在 __getattr__ 中返回默认值时 Python pickle 崩溃
【发布时间】:2011-06-20 18:51:13
【问题描述】:

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

我希望它返回无。

【问题讨论】:

    标签: python python-2.7 pickle


    【解决方案1】:

    实现__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)
    

    我通常会遍历所有以下划线开头的名称,这样我就可以避开内部符号的魔力。

    【讨论】:

    • 我认为这可能最适合我的情况,因为 _ 和 __ 永远不会是属性名称。
    • 这真是太聪明了,我从来没想过这样做
    • 哥们,你在这里救了我这么多
    【解决方案2】:

    当您的类中不存在某个属性时,您需要提出AttributeError

    def __getattr__(self, key):
        i = self.__getitem__(key)
        if i == None:
            raise AttributeError
        return self.__getitem__(key)
    

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

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

    我想你可以尝试将pickle 正在寻找的所有特殊方法定义为虚拟方法?

    【讨论】:

    • @Nix:哦,我没看到你最后的评论
    • 可能无法获得我想要的东西......我只是想绕过每次都检查 hasattr。
    • @Nix:Ferdinand 发布了一个答案,可以通过调用父类默认方法来解决问题。
    • 我刚看到,感谢您的帮助 +1 解释了我的疏忽(有些人可能以编程方式获取 getattr 异常的关键)。
    猜你喜欢
    • 2023-03-21
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多