【问题标题】:how to make classes with __getattr__ pickable如何使用 __getattr__ 使课程可挑选
【发布时间】:2018-03-20 09:15:21
【问题描述】:

如何修改下面的类以使它们可以选择?

这个问题:How to make a class which has __getattr__ properly pickable?类似,但是在getattr的使用中引用了错误的异常。

这个另一个问题似乎提供了有意义的见解Why does pickle.dumps call __getattr__?,但是它没有提供一个例子,老实说我无法理解我应该实现什么。

import pickle
class Foo(object):
    def __init__(self, dct):
        for key in dct:
            setattr(self, key, dct[key])


class Bar(object):
    def __init__(self, dct):
        for key in dct:
            setattr(self, key, dct[key])

    def __getattr__(self, attr):
        """If attr is not in channel, look in timing_data
        """
        return getattr(self.foo, attr)

if __name__=='__main__':
    dct={'a':1,'b':2,'c':3}
    foo=Foo(dct)
    dct2={'d':1,'e':2,'f':3,'foo':foo}
    bar=Bar(dct2)
    pickle.dump(bar,open('test.pkl','w'))
    bar=pickle.load(open('test.pkl','r'))

结果:

     14         """If attr is not in channel, look in timing_data
     15         """
---> 16         return getattr(self.foo, attr)
     17
     18 if __name__=='__main__':

RuntimeError: maximum recursion depth exceeded while calling a Python object

【问题讨论】:

    标签: python python-2.7 pickle getattr


    【解决方案1】:

    这里的问题是您的__getattr__ 方法实现得很差。它假定self.foo 存在。如果self.foo 不存在,尝试访问它最终会调用__getattr__ - 这会导致无限递归:

    >>> bar = Bar({})  # no `foo` attribute
    >>> bar.x
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "untitled.py", line 19, in __getattr__
        return getattr(self.foo, attr)
      File "untitled.py", line 19, in __getattr__
        return getattr(self.foo, attr)
      File "untitled.py", line 19, in __getattr__
        return getattr(self.foo, attr)
      [Previous line repeated 329 more times]
    RecursionError: maximum recursion depth exceeded while calling a Python object
    

    要解决此问题,如果不存在 foo 属性,则必须抛出 AttributeError:

    def __getattr__(self, attr):
        """If attr is not in channel, look in timing_data
        """
        if 'foo' not in vars(self):
            raise AttributeError
        return getattr(self.foo, attr)
    

    (我使用vars 函数来获取对象的字典,因为它看起来比self.__dict__ 更好。)


    现在一切都按预期进行:

    dct={'a':1,'b':2,'c':3}
    foo=Foo(dct)
    dct2={'d':1,'e':2,'f':3,'foo':foo}
    bar=Bar(dct2)
    data = pickle.dumps(bar)
    bar = pickle.loads(data)
    print(vars(bar))
    # output:
    # {'d': 1, 'e': 2, 'f': 3, 'foo': <__main__.Foo object at 0x7f040fc7e7f0>}
    

    【讨论】:

    • 对第一段添加更多解释:__getattr__ 仅在无法以“通常方式”找到属性时调用,例如检查对象的__dict__。这就是为什么__getattr__ 中的self.foo 不会一直 给你无限递归,而是只有当self.foo__dict__ 中找不到时。
    猜你喜欢
    • 2018-03-17
    • 2012-06-01
    • 2015-06-10
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    • 2016-01-09
    • 1970-01-01
    相关资源
    最近更新 更多