python的描述符(descriptor)、装饰器(property)造成的一个无限递归问题分享

yipeiwu_com6年前Python基础

分享一下刚遇到的一个小问题,我有一段类似于这样的python代码:

复制代码 代码如下:

# coding: utf-8

class A(object):

    @property
    def _value(self):
#        raise AttributeError("test")
        return {"v": "This is a test."}

    def __getattr__(self, key):
        print "__getattr__:", key
        return self._value[key]

if __name__ == '__main__':
    a = A()
    print a.v


运行后可以得到正确的结果
复制代码 代码如下:

__getattr__: v
This is a test.

但是注意,如果把
复制代码 代码如下:

#        raise AttributeError("test")


这行的注释去掉的话,即在_value方法里面抛出AttributeError异常,事情就会变得有些奇怪。程序运行的时候并不会抛出异常,而是会进入一个无限递归:

复制代码 代码如下:

File "attr_test.py", line 12, in __getattr__
    return self._value[key]
  File "attr_test.py", line 12, in __getattr__
    return self._value[key]
RuntimeError: maximum recursion depth exceeded while calling a Python object

通过多方查找后发现是property装饰器的问题,property实际上是一个descriptor。在python doc中可以发现这样的文字:

复制代码 代码如下:

object.__get__(self, instance, owner)

Called to get the attribute of the owner class (class attribute access) or of an instance of that class (instance attribute access). owner is always the owner class, while instance is the instance that the attribute was accessed through, or None when the attribute is accessed through the owner. This method should return the (computed) attribute value or raise an AttributeError exception.

这样当用户访问._value时,抛出了AttributeError从而调用了__getattr__方法去尝试获取。这样程序就变成了无限递归。

这个问题看上去不复杂,但是当你的_value方法是比较隐晦的抛出AttributeError的话,调试起来就会比较困难了。

相关文章

浅谈MySQL中的触发器

在很多时候,干得利索不如想的明白。方案应该根据场景来设计,不是盲目的依靠经验,当然这也算新经验!    需求是把公司的几套公共系统做成通过邮箱用户名和密码认证,只需记...

python程序快速缩进多行代码方法总结

python程序快速缩进多行代码方法总结

该语言中缩进是其精髓,通过缩进可以表示函数、循环等程序结构的范围。有时写完程序后,发现所有程序需要放入函数def中,这时就需要对一整块程序同时进行缩进,还有其他一些情况,也会需要多行整体...

Python之NumPy(axis=0 与axis=1)区分详解

Python之NumPy(axis=0 与axis=1)区分详解

python中的axis究竟是如何定义的呢?他们究竟代表是DataFrame的行还是列?考虑以下代码: >>>df = pd.DataFrame([[1, 1,...

python 读写txt文件 json文件的实现方法

首先第一步,打开文件,有两个函数可供选择:open() 和  file() ①. f = open('file.txt',‘w')    ... &nbs...

python 根据网易云歌曲的ID 直接下载歌曲的实例

python 根据网易云歌曲的ID 直接下载歌曲的实例

特么的,上次写了一堆,发现,原来下载网易云的歌曲根本不用这么费劲,直接用! http://music.163.com/song/media/outer/url?id=这里填歌曲i...