一、 引言

《第7.23节 Python使用property函数定义属性简化属性访问的代码实现》《第7.26节 Python中的@property装饰器定义属性访问方法getter、setter、deleter 详解》中介绍了两种设置属性访问方法,通过设置可以在相关属性访问时调用对应的方法执行访问,但这种方法只能针对每个属性去设置,由于没有参数能给出当前访问的属性名,因此不同属性必须调用不同的方法,并且上述方法都是为了不支持简单访问的属性设置简单访问方法(如对象名.属性名、赋值语句)、或者为了控制访问逻辑使用的。而上节介绍的__getattribute__方法是所有属性查看都会调用的方法,并且参数支持“属性名”的传入,那是不是可以通过重写该方法实现所有属性查看的逻辑控制呢?答案是肯定的。

二、 重写__ getattribute 方法

要重写
getattribute __方法,就需要在自定义类中定义该方法。定义语法如下:

__ getattribute __(self, 属性名)

该方法返回正常情况应该是对应属性的值,但开发者可以对返回值进行控制,例如浮点数强制返回两位小数等。实际上我们可以认为该方法是截获所有通过“实例名.属性名”访问实例变量、类变量、实例方法的所有操作。

注意,重写方法中:

  1. 代码如果需要调用__ getattribute __获取属性对应的数据,不能通过”self. __ getattribute __”或”self.属性”或“实例名.__dict__[“属性名”]”去获取数据;

    1)“self.__ getattribute __”会重复触发__ getattribute __的递归调用,因此应该通过父类的__ getattribute __方法去返回数据,即调用super().__getattribute__(name)

    2)”self.属性”这种方式,由于参数传递的属性名是一个字符串,”self.属性”无法使用;

    3)“实例名.__dict__[“属性名”]”去获取数据这种方式存在至少3个问题:

    a)一是对__dict__属性进行访问会重复触发__ getattribute __的递归调用;

    b)二是实例方法的访问无法通过__dict__[属性名]方式访问,因为__dict__中只保留了实例变量,没有保留方法名和类变量,而方法调用以及通过实例访问类变量也会触发__ getattribute __;

    c)三是这种访问方式无法访问父类继承的属性。
  2. 如果需要对数据进行逻辑处理,可以将通过父类的__ getattribute __方法取得的数据进行加工处理后再输出。

三、 重写__ getattribute __方法的案例

  1. 案例说明

    本案例定义类Car,其中有一个类变量refcount,二个实例变量power、totaldistance、三个实例方法(构造方法、__ getattribute__方法、drive方法),在__ getattribute__方法中将所有访问实例属性的属性名输出,并调用父类object类的__ getattribute__方法返回真正的数据,代码中对数据并没有进行处理。

    类定义后,进行相关属性访问,查看属性数据时看看__ getattribute__方法是否执行。
  2. 源代码及执行反馈信息
>>> class Car():
refcount = 0;
def __init__(self, power):
print("In Car init,objectid = {:#016X}".format(id(self)))
self.power = power
self.totaldistance = 0
Car.refcount+=1 def drive(self,distance): #方法的执行也会触发__getattribute__
#drive方法会触发两次__getattribute__方法
#一是赋值语句本身要读取totaldistance,二是print语句中要读取
self.totaldistance += distance
print("You drived {}KM,totaldistance={}KM".format(distance,self.totaldistance)) def __getattribute__(self, properyname):
print("In __getattribute__,you are getting properyty: {}".format(properyname))
#注意必须调用父类(本方法中是object类)的__getattribute__返回对应值
#如果要对对应值修改可以先用局部变量记录父类返回值,修改该值后再返回
return object.__getattribute__(self, properyname) >>> car = Car('汽油发动机')
In Car init,objectid = 0X000000038CAB38
>>> car.power #直接访问实例的实例变量会触发__ getattribute __方法
In __getattribute__,you are getting properyty: power
'汽油发动机'
>>> car.drive(107) #调用方法会触发会触发__ getattribute __方法,方法中对实例变量的访问也会触发
In __getattribute__,you are getting properyty: drive
In __getattribute__,you are getting properyty: totaldistance
In __getattribute__,you are getting properyty: totaldistance
You drived 107KM,totaldistance=107KM
>>> In __getattribute__,you are getting properyty: __dict__
In __getattribute__,you are getting properyty: __class__
car.totaldistance #输入”car.”后解释器弹窗弹出实例的实例变量供选择会触发 In __getattribute__,you are getting properyty: totaldistance
107
>>> Car.refcount #通过类访问类变量访问不触发__ getattribute __方法
1
>>> car.refcount #通过实例访问类变量访问会触发__ getattribute __方法
In __getattribute__,you are getting properyty: refcount
1
>>>
  1. 界面执行截图

截图说明:

  1. 本截图中的类与源代码中的类多了个基类Vehicle,实际上这个基类在演示中没有使用,完全可以去掉,之所以没报错是因为解释器在本案例之外执行了该基类的定义语句。之所以没有重新截图主要是因为下面的原因;
  2. 该图是合成的,为什么要合成,是为了说明截图中标黄色部分的内容:

    1> 图中标黄色部分的内容不是代码中的内容,而是__getattribute__输出的信息;

    2> 这个信息的触发是老猿在执行标黄色部分的下一代码“car.totaldistance”在输入完“car.”后等待了一下,此时解释器自动弹出相关属性供选择时触发的(具体弹出请见截图中弹框部分),可以看到这个弹窗触发了__dict__和__class__的访问;

    3> 本案例之前,老猿没有意识到这个输出,这个输出信息出现在输入信息的上方,本来输入的“car.”是在“>>>”后的。

四、 案例总结

通过上述案例的分析,可以得出如下结论:

  1. 通过类体外”实例变量.属性”、类体内实例方法中“self.属性”都会触发__getattribute__方法;

  2. 通过类体外”实例变量.方法()”、类体内实例方法中调用本实例的方法都会触发__getattribute__方法;

  3. 通过“类名.类变量”访问类变量不会触发__getattribute__方法,通过“实例.类变量” 访问类变量会触发__getattribute__方法;

  4. 通过解释器弹窗选择属性也会触发__getattribute__方法;

  5. 上面截图对如下结论没有验证:通过Python解释器的type、help、dir以及特殊变量访问都会触发__getattribute__方法。补充截图如下:

    以上情况证明,任何通过实例对实例属性(含方法)、类属性的访问都会触发__getattribute__方法。

    本节介绍了__getattribute__方法的重写,通过重写这个方法执行属性查看证明,所有通过“实例名”读取实例属性和类属性都会触发__getattribute__方法的执行。因此如果有需要可以修改这个方法,控制相关属性的数据输出。

老猿Python,跟老猿学Python!

博客地址:https://blog.csdn.net/LaoYuanPython


请大家多多支持,点赞、评论和加关注!谢谢!

第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获的更多相关文章

  1. 第8.6节 Python类中的__new__方法深入剖析:调用父类__new__方法参数的困惑

    上节<第8.5节 Python类中的__new__方法和构造方法__init__关系深入剖析:执行顺序及参数关系案例详解>通过案例详细分析了两个方法的执行顺序,不知大家是否注意到了,在上述 ...

  2. 第8.34节 《Python类中常用的特殊变量和方法》总结

    本章介绍了Python类中常用的特殊变量和方法,这些特殊变量和方法都有特殊的用途,是Python强大功能的基石之一,许多功能非常有Python特色.由于Python中一切皆对象,理解这些特殊变量和方法 ...

  3. 重写Object类中的equals方法

    Object是所有类的父亲,这个类有很多方法,我们都可以直接调用,但有些方法并不适合,例如下面的student类 public class Student { //姓名.学号.年纪 private S ...

  4. python -- 类中--内置方法

    isinstance 和  issubclass isinstance(obj,b)  检查是否obj是否是类b的对象 class A(object):pass class B(A):pass b=B ...

  5. 第8.14节 Python类中内置方法__str__详解

    一. object类内置方法__str__和函数str 类的内置方法__str__和内置函数str实际上实现的是同一功能,实际上str调用的就是__str__方法,只是调用方式不同,二者的调用语法如下 ...

  6. 第8.13节 Python类中内置方法__repr__详解

    当我们在交互环境下输入对象时会直接显示对象的信息,交互环境下输入print(对象)或代码中print(对象)也会输出对象的信息,这些输出信息与两个内置方法:__str__方法和__repr__方法有关 ...

  7. 第7.5节 揭开Python类中self的面纱

    在上节已经引入介绍了类定义,并简单介绍了类变量.实例变量.类方法和实例方法,后面章节还会进一步详细介绍相关的内容.本节要介绍的self是与类变量.实例变量.类方法和实例方法具体定义实现强相关的. 一. ...

  8. 第8.5节 Python类中的__new__方法和构造方法__init__关系深入剖析:执行顺序及参数关系案例详解

    上节介绍了__new__()方法这个比构造方法还重要的方法的语法,本节通过案例来详细剖析__new__()方法的细节以及它与构造方法之间的关系. 一.    案例说明 本节以圆Cir类为例来说明,为了 ...

  9. 第8.30节 重写Python __setattr__方法实现属性修改捕获

    一. 引言 在<第8.26节 重写Python类中的__getattribute__方法实现实例属性访问捕获>章节介绍了__getattribute__方法,可以通过重写该方法,截获所有通 ...

随机推荐

  1. mac os 10.15安装jdk 1.6

    1.如果出现报错 已经安装了最高版本 下载请看:https://www.jianshu.com/p/3b580c405c7c 请看下面方法处理错误 1.在mac的访达中 找到 "脚本编辑器& ...

  2. python数据分析02语法基础

    在我来看,没有必要为了数据分析而去精通Python.我鼓励你使用IPython shell和Jupyter试验示例代码,并学习不同类型.函数和方法的文档.虽然我已尽力让本书内容循序渐进,但读者偶尔仍会 ...

  3. JS超酷时钟的制作

    通过补充代码,实现时钟实时显示当前时间:年.月.日.时.分.秒.日期. <!DOCTYPE html> <html> <head lang="zh-CN&quo ...

  4. C#调用pyd

    python打包成pyd在本篇博客不多叙述,请读者自行百度,本篇博客主要讲解在C#中如何调用pyd以及遇到的一些问题如何解决. 1.安装pythonnet pythonnet是一个强大的工具包,用于C ...

  5. python3:文件读写+with open as语句(转)

    读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直接操作磁盘, ...

  6. HTTP 报文格式简介

    HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送 WWW 方式的数据,关于 HTTP 协议的详细内容请参考 RFC2616.HTTP 协议采用了请求/响 ...

  7. 03、JDBC范例

    范例:JDBC查询 package com.hsp; import java.sql.Connection; import java.sql.DriverManager; import java.sq ...

  8. C#设计模式-外观模式(Facade Pattern)

    引言 在软件测试中,一般都是在功能测试稳定的情况下再进行UI自动化测试.或者进行性能测试.如果一个一个进行太麻烦,此时可以使用对外提供一个简单接口,通过这个接口可以访问内部一群接口.例如进行UI自动化 ...

  9. Python学习第三天 --- 分支、循环、条件、枚举

    1.表达式: 表达式(Expression)是运算符(operator)和操作数(operand)所构成的序列. 2.表达式的优先级: 3.python的注释: #单行注释 ''' 多行注释 ''' ...

  10. hectf2020部分简单题题解wp

    HECTF 我真是又菜又没时间肝题..又又又只水了波简单题... Reverse 1.Hello_Re file查一波 32bit,拖进IDA中 老规矩shift+F12 查看字符串: 跳转 F5查看 ...