【问题标题】:Why there is infinite recursion loop risk in __getattribute__?为什么 __getattribute__ 中存在无限递归循环风险?
【发布时间】:2018-10-09 19:56:31
【问题描述】:

参考现有问题的第二个最佳答案:Difference between __getattr__ vs __getattribute__,其中包括某人建议的代码:

class Count(object):
    def __init__(self, mymin, mymax):
        self.mymin = mymin
        self.mymax = mymax
        self.current = None

    def __getattr__(self, item):
        self.__dict__[item] = 0
        return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return super(Count, self).__getattribute__(item)

obj1 = Count(1, 10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

我的问题是:

当我运行代码时,它确实没有遇到无限递归深度(以超出最大递归深度结束)。为什么?而且,如果我将代码 super(Count, self).__getattribute__(item) 更改为 super(object, self).__getattribute__(item),它确实会陷入无限循环。为什么又来了?

请用详细的调用过程说明原因。

【问题讨论】:

  • 不相关,但更新对象的状态 - 并最终创建新属性 - 在属性查找上是一个非常糟糕的主意(除非你只在受保护的属性中缓存一些值,但这里不是这种情况)
  • "当我运行代码时,它没有遇到无限递归" => 为什么会这样???
  • @brunodesthuilliers 我将“return super(Count, self).__getattribute__(item)”这一行更改为“return super(object, self).__getattribute__(item)”,然后就成功了。
  • 你永远不应该写super(object, self)。曾经。那永远不可能是正确的。 上面没有任何东西 object.

标签: python


【解决方案1】:

我将尝试通过将self.__dict__[item] 分成两部分来使其更简单:

class Count(object):
    def __getattr__(self, item):
        print('__getattr__:', item)
        d = self.__dict__
        print('resolved __dict__')
        d[item] = 0
        return 0

    def __getattribute__(self, item):
        print('__getattribute__:', item)
        if item.startswith('cur'):
            raise AttributeError
        return super(Count, self).__getattribute__(item)

obj1 = Count()
print(obj1.current)

输出是

__getattribute__: current
__getattr__: current
__getattribute__: __dict__
resolved __dict__
0

现在,如果我们将super(Count, self) 替换为不正确的构造 super(object, self),则不会打印消息。这是因为__getattribute__ 也会屏蔽对__dict__ 的访问。然而super 对象将指向不存在的object 的基类,因此我们的__getattribute__ 函数将始终抛出AttributeError

现在,在__getattribute__ 失败后,正在尝试__getattr__ ......好吧,它不只是将__dict__ 解析为某个值,而是尝试将其作为属性获取 - 最终再次调用__getattribute__ 因此我们得到了。

....
__getattribute__:  __dict__
__getattr__: __dict__
__getattribute__:  __dict__
__getattr__: __dict__
__getattribute__:  __dict__
__getattr__: __dict__
__getattribute__:  __dict__
__getattr__: __dict__
__getattribute__:  __dict__
__getattr__: __dict__
Traceback (most recent call last):
  File "getattribute.py", line 15, in <module>
    print(obj1.current)
  File "getattribute.py", line 4, in __getattr__
    d = self.__dict__
  File "getattribute.py", line 4, in __getattr__
    d = self.__dict__
  File "getattribute.py", line 4, in __getattr__
    d = self.__dict__
  [Previous line repeated 328 more times]
  File "getattribute.py", line 8, in __getattribute__
    print('__getattribute__: ', item)
RecursionError: maximum recursion depth exceeded while calling a Python object

如果您使用setattr(self, item, 0) 而不是查找self.__dict__,这本可以“避免”:

class Count(object):
    def __getattr__(self, item):
        setattr(self, item, 0)
        return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return super(object, self).__getattribute__(item)

obj1 = Count()
print(obj1.current)

当然这样的代码是不正确的——尽管如此,尝试访问任何其他属性都会失败。

【讨论】:

  • 为什么 getattribute 会屏蔽对 dict 的访问?我试过你的代码,我认为代码永远不会到达 print 语句,但它会到达“d = self.__dict__”。
  • 我看到你在解释上加了,我的问题完全解决了,谢谢!
  • 似乎 'object' 没有名为 dict 的属性,而任何其他继承它的类都有。当我使用 super(Count, self) 时,self.__dict__ 可以被访问,因为 dict 是 Count 类的一个属性。我理解正确吗?
  • 另外,“尝试访问任何其他属性都会失败”是什么意思
猜你喜欢
  • 1970-01-01
  • 2019-11-12
  • 2014-03-24
  • 1970-01-01
  • 2018-12-03
  • 2016-10-06
  • 1970-01-01
  • 2014-10-13
  • 1970-01-01
相关资源
最近更新 更多