通常情况下,我们在访问类或者实例对象的时候,会牵扯到一些属性访问的魔法方法,主要包括:

① __getattr__(self, name): 访问不存在的属性时调用

② __getattribute__(self, name):访问存在的属性时调用(先调用该方法,查看是否存在该属性,若不存在,接着去调用①)

③ __setattr__(self, name, value):设置实例对象的一个新的属性时调用

④ __delattr__(self, name):删除一个实例对象的属性时调用

为了验证以上,现列出代码如下:

 class Test:
def __getattr__(self, name):
print('__getattr__') def __getattribute__(self, name):
print('__getattribute__') def __setattr__(self, name, value):
print('__setattr__') def __delattr__(self, name):
print('__delattr__') >>> t=Test()
>>> t.x
__getattribute__

如上述代码所示,x并不是Test类实例t的一个属性,首先去调用 __getattribute__() 方法,得知该属性并不属于该实例对象;但是,按照常理,t.x应该打印 __getattribute__ 和__getattr__,但实际情况并非如此,为什么呢?难道以上Python的规定无效吗?

不要着急,听我慢慢道来!

实例对象属性寻找的顺序如下:

① 首先访问 __getattribute__() 魔法方法(隐含默认调用,无论何种情况,均会调用此方法)

② 去实例对象t中查找是否具备该属性: t.__dict__ 中查找,每个类和实例对象都有一个 __dict__ 的属性

③ 若在 t.__dict__ 中找不到对应的属性, 则去该实例的类中寻找,即 t.__class__.__dict__

④ 若在实例的类中也招不到该属性,则去父类中寻找,即 t.__class__.__bases__.__dict__中寻找

⑤ 若以上均无法找到,则会调用 __getattr__ 方法,执行内部的命令(若未重载 __getattr__ 方法,则直接报错:AttributeError)

以上几个流程,即完成了属性的寻找。

但是,以上的说法,并不能解释为什么执行 t.x 时,不打印 ’__getattr__'  啊?

你看,又急了不是,作为一名程序猿,一定要有耐心 ^_^

问题就出在了步骤的第④步,因为,一旦重载了 __getattribute__() 方法,如果找不到属性,则必须要手动加入第④步,否则无法进入到 第⑤步 (__getattr__)的。

验证一下以上说法是否正确:

方法一:采用object(所有类的基类)

 class Test:
def __getattr__(self, name):
print('__getattr__') def __getattribute__(self, name):
print('__getattribute__')
object.__getattribute__(self, name) def __setattr__(self, name, value):
print('__setattr__') def __delattr__(self, name):
print('__delattr__') >>> t=Test()
>>> t.x
__getattribute__
__getattr__

怎么样,显示出来了吧?哈哈

方法二:采用 super() 方法

 class Test:
def __getattr__(self, name):
print('__getattr__') def __getattribute__(self, name):
print('__getattribute__')
super().__getattribute__(name) def __setattr__(self, name, value):
print('__setattr__') def __delattr__(self, name):
print('__delattr__') >>> t=Test()
>>> t.x
__getattribute__
__getattr__

哈哈,酱紫也可以哦 ^v^

那么方法一和方法二有什么不同呢?仔细看一下你就会发现,其实就是很小的一点不同而已:

 #方法一:使用基类object的方法
def __getattribute__(self, name):
print('__getattribute__')
object.__getattribute__(self, name) #方法二:使用super()方法(有的认为super()是类,此处暂以方法处理)
def __getattribute__(self, name):
print('__getattribute__')
super().__getattribute__(name)

在Python2.x中,以上super的用法应该改为 super(Test, self).xxx,但3.x中,可以像上面代码一样简单使用。

那么super到底是什么东西呢?如果想详细的理解,请点击这里

哈哈,以上介绍完毕,那么 __setattr__ 和 __delattr__ 方法相对简单了多了:

 class Test:
def __getattr__(self, name):
print('__getattr__') def __getattribute__(self, name):
print('__getattribute__')
object.__getattribute__(self, name) def __setattr__(self, name, value):
print('__setattr__') def __delattr__(self, name):
print('__delattr__') >>> t=Test()
>>> t.x=10
__setattr__
>>> del t.x
__delattr__

至此,关于属性访问,先告一段落,后续会有更高级的descirptor。

对了,再补充一点哈!

 class Test:
def __init__(self):
self.count = 0
def __setattr__(self, name, value):
print('__setattr__')
self.count += 1 >>> t=Test()
__setattr__
Traceback (most recent call last):
File "<pyshell#364>", line 1, in <module>
t=Test()
File "<pyshell#363>", line 3, in __init__
self.count = 0
File "<pyshell#363>", line 6, in __setattr__
self.count += 1
AttributeError: 'Test' object has no attribute 'count'

为什么会出现上述情况呢?我还没有调用__setattr__()呢,只是单纯的定义了一个实例而已? @_@(咋回事?幻觉吗)

看报错信息很容易明白,这是因为:

① __init__()时,给内部属性 self.count进行了赋值;

② 赋值默认调用 __setattr__() 方法

③ 当调用 __setattr__()方法时,首先打印 '__setattr__'字符串,而后执行 self.cout += 1操作

④ 当执行 self.cout 加 1 操作时,将会去寻找 count 这个属性,然而,由于此时 __init__尚未完成,并不存在 count这个属性,因此导致 'AttributeError' 错误

那么该如何更改呢?可以这样的:

 class Test:
def __init__(self):
self.count = 0
def __setattr__(self, name, value):
print('__setattr__')
super().__setattr__(name, value+1) >>> t=Test()
__setattr__
>>> t.count
1

如何,问题解决了吧!

但是以上代码虽然解决了报错的问题,深入体会一下,你会发现,采用此方法只是给 基类object增加了一个属性 count,而并不是实例的属性,因此,以上这种写法避免使用

另外,再次将代码改进一下,如下:

 class Test:
def __setattr__(self, name, value):
self.name = value >>> t=Test()
>>> t.x=1
Traceback (most recent call last):
File "<pyshell#413>", line 1, in <module>
t.x=1
File "<pyshell#411>", line 3, in __setattr__
self.name = value
File "<pyshell#411>", line 3, in __setattr__
self.name = value
File "<pyshell#411>", line 3, in __setattr__
self.name = value
[Previous line repeated 327 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object

居然报错了,看报错信息为 “递归错误”,我没用递归啊,怎么会有这个错误呢?

其实,原因很简单:当我们给 t.x 赋值时,调用了 __setattr__()方法,进入该方法;该方法中,又来了一次赋值(self.name = value),又会去调用 __setattr__() 方法,持续这个死循环(子子孙孙无穷尽也,必须要有一代断子绝孙);

我们知道,系统的资源是有限的,丫的你老是申请资源不释放,系统哪来的那么多资源给你自己用?因此,Python解释器规定,递归深度不得超过200(不同版本不一样),你超过了,不好意思,不带你玩了!

所以,我们只好改变上述的问题了:

 t.x=2
>>> class Test:
def __setattr__(self, name, value):
print('__setattr__() been called')
super().__setattr__(name, value) >>> t=Test()
>>> t.x=1
__setattr__() been called
>>> t.x=2
__setattr__() been called

OK,至此,关于属性操作的问题暂时完结吧!

Python魔法方法之属性访问 ( __getattr__, __getattribute__, __setattr__, __delattr__ )的更多相关文章

  1. python魔法方法:__getattr__,__setattr__,__getattribute__

    python魔法方法:__getattr__,__setattr__,__getattribute__ 难得有时间看看书....静下心来好好的看了看Python..其实他真的没有自己最开始想的那么简单 ...

  2. python 魔法方法补充(__setattr__,__getattr__,__getattribute__)

    python 魔法方法补充 1 getattribute (print(ob.name) -- obj.func())当访问对象的属性或者是方法的时候触发 class F(object): def _ ...

  3. Python魔法方法__getattr__和__getattribute__详解

    在Python中有这两个魔法方法容易让人混淆:__getattr__和getattribute.通常我们会定义__getattr__而从来不会定义getattribute,下面我们来看看这两个的区别. ...

  4. python基础教程_学习笔记11:魔法方法、属性和迭代器

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/signjing/article/details/31417309 魔法方法.属性和迭代器 在pyth ...

  5. 【python学习笔记】9.魔法方法、属性和迭代器

    [python学习笔记]9.魔法方法.属性和迭代器 魔法方法:xx, 收尾各有两个下划线的方法 __init__(self): 构造方法,创建对象时候自动执行,可以为其增加参数, 父类构造方法不会被自 ...

  6. python 魔法方法(学习过程的笔记)

    有小伙伴会问,什么是python的魔法方法,python的魔法方法有什么用呢, 它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法"的特殊方法. 它们经常是两 ...

  7. Python魔法方法(magic method)细解几个常用魔法方法(上)

    这里只分析几个可能会常用到的魔法方法,像__new__这种不常用的,用来做元类初始化的或者是__init__这种初始化使用的 每个人都会用的就不介绍了. 其实每个魔法方法都是在对内建方法的重写,和做像 ...

  8. python魔法方法大全

    1.python魔法方法详解: python魔法方法是可以修改重载的,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而 ...

  9. with上下文管理 python魔法方法

    with语法在Python里很常见, 主要的利好是使用代码更简洁. 常见的使用场景有: 1. 资源对象的获取与释放. 使用with可以简化try...finally ... 2. 在不修改函数代码的前 ...

随机推荐

  1. Asp.net Core 和类库读取配置文件信息

    Asp.net Core 和类库读取配置文件信息 看干货请移步至.net core 读取配置文件公共类 首先开一个脑洞,Asp.net core 被使用这么长时间了,但是关于配置文件(json)的读取 ...

  2. .net core 生成二维码

    其实生成二维码的组件有很多种,如:QrcodeNet,ZKWeb.Fork.QRCoder,QRCoder等 我选QRCoder,是因为小而易用.支持大并发生成请求.不依赖任何库和网络服务. 既然是. ...

  3. Unreal Open Day游记

    前几天去参加了Unreal Open Day,周四早上从北京出发,坐地铁跟徐导,呵呵,simon他们汇合后,打车去了北京南站.一路上有小雨,不禁让人多少有点担心堵车,好在一路顺利.由于还没有一台较牛的 ...

  4. ovs-vsctl 命令详解

    Open vSwitch中有多个命令,分别有不同的作用,大致如下: ovs-vsctl用于控制ovs db ovs-ofctl用于管理OpenFlow switch 的 flow ovs-dpctl用 ...

  5. Xcode的多种Build Configuration

    一: 建多个Configuration的目的. 多套域名打包. 1 开发时的域名. 2 内测时的域名. 3 公测时的域名. 4 企业版的域名. 5 APP Store的域名. 通过注释的方式,容易出错 ...

  6. jzoj5710

    對於每個詢問,我們先考慮插入1~r中每個數的答案,這樣子就不用考慮後面數對現在的影響 那麼我們可以在權值線段樹的每一個位置存儲每一個值最後一次結束的位置 問題變成了,如何判斷區間連續? 我們可以對線段 ...

  7. FunDA(10)- 用户功能函数模式:User Function Model

    前面我们提过:FunDA就像一个管道(PipeLine).管道内流动着一串数据(Data)或者运算指令(Action).管道的源头就是能产生纯数据的数据源(Source),跟着在管道的中间会有一些节点 ...

  8. js闭包之我见

    很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码: function test(){ for(var i=0;i<5;i++){ window.onclick=function( ...

  9. ReentrantReadWriteLock源码分析(一)

    此处源码分析,主要是基于读锁,非公平机制,JDK1.8. 问题: 1.ReentrantReadWriteLock是如何创建读锁与写锁? 2.读锁与写锁的区别是什么? 3.锁的重入次数与获取锁的线程数 ...

  10. Python小白学习之路(十)—【函数】【函数返回值】【函数参数】

    写在前面: 昨天早睡之后,感觉今天已经恢复了百分之八十的样子 又是活力满满的小伙郭 今日份鸡汤: 我始终相信,在这个世界上,一定有另一个自己,在做着我不敢做的事,在过着我想过的生活.-------宫崎 ...