属性访问引发的无限递归

栏目: Python · 发布时间: 6年前

内容简介:这篇 中介绍了几种属性访问的区别,其中在由于所以常常使用

起步

这篇 中介绍了几种属性访问的区别,其中在 __getattribute__ 中提到 使用基类的方法来获取属性能避免在方法中出现无限递归的情况 。 那么那些情况会引发无限递归呢?

getattribute 引发的无限递归

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0
        else:
            return self.__dict__[name]

d = D()
print(d.test)
print(d.test2)

由于 __getattribute__() 方法是无条件触发,因此在访问 self.__dict__ 时,会再次调用 __getattribute__ ,从而引发无限递归。

所以常常使用 object 基类的 __getattribute__ 来避免这种情况:

def __getattribute__(self,name):
    if name=='test':
        return 0.
    else:
        return object.__getattribute__(self, name)

getattr 引发的无限递归

import copy

class Tricky(object):
    def __init__(self):
        self.special = ["foo"]

    def __getattr__(self, name):
        if name in self.special:
            return "yes"
        raise AttributeError()

t1 = Tricky()
assert t1.foo == "yes"
t2 = copy.copy(t1)
assert t2.foo == "yes"
print("This runs, but isn't covered.")

这里存在了无限递归,你会发现最后两行代码没有执行。 __getattr__ 函数是当属性不存在时被调用的,而函数里使用的唯一属性是 self.special 。但它是在 __init__ 中创建的,所以它应该始终存在,是吧?

答案在于 copy.copy 的工作方式上。复制对象时,它不会调用其 __init__ 方法。它创建一个新的空对象,然后将属性从旧复制到新的。为了实现自定义复制,对象可以提供执行复制的功能,因此复制模块在对象上查找这些属性。这自然会调用 __getattr__

如果在 __getattr__ 添加一些输出追踪:

def __getattr__(self, name):
    print(name)
    if name in self.special:
        return "yes"
    raise AttributeError()

得到的输出是:

foo
__getstate__
__setstate__
special
special
special
special
special
...

这里发生的过程是:复制模块查找 __setstate__ 属性,该属性不存在,因此调用了 __getattr__ 。新对象是空白的,它没有调用 __init__ 来创建 self.special 。由于该属性不存在,因此调用 __getattr__ ,并开始无限递归。

Traceback (most recent call last):
  File "E:/workspace/test3.py", line 15, in <module>
    t2 = copy.copy(t1)
  File "E:\workspace\.env\lib\copy.py", line 106, in copy
    return _reconstruct(x, None, *rv)
  File "E:\workspace\.env\lib\copy.py", line 281, in _reconstruct
    if hasattr(y, '__setstate__'):

从异常信息可以看出,引发这次雪崩的导火线是 hasattr(y, '__setstate__') 。 在这个案例中,函数可以这样修改:

def __getattr__(self, name):
    if name == "special":
        raise AttributeError()
    if name in self.special:
        return "yes"
    raise AttributeError()

以上所述就是小编给大家介绍的《属性访问引发的无限递归》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

暗网

暗网

杰米·巴特利特 / 刘丹丹 / 北京时代华文书局 / 2018-7 / 59.00

全面深入揭秘“黑暗版淘宝”暗网的幕后世界和操纵者 现实中所有的罪恶,在暗网中,都是明码标价的商品。 暗杀、色情、恋童癖、比特币犯罪、毒品交易…… TED演讲、谷歌特邀专家、英国智库网络专家杰米•巴特利特代表作! 1、 被大家戏称为“黑暗版淘宝”的暗网究竟是什么?微信猎奇 文不能告诉你的真相都在这里了! 2、 因章莹颖一案、Facebook信息泄露危机而被国人所知的暗网......一起来看看 《暗网》 这本书的介绍吧!

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具