一、 引言

《第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. 09-jQuery案例:爱好选择器

    爱好选择器HTML 1 <!DOCTYPE html> 2 <head> 3 <meta charset="UTF-8"> 4 <titl ...

  2. layui表单提交与ajax访问webapi

    啊啊啊啊 这个东西实在很蛋疼啊 每次访问webapi就很老火 这里就一下  以后忘记的话就来查阅 不多说 直接开始 首先html页面 新建一个基于layui的form表单页面LayuiForm.csh ...

  3. 利用 Docker 构建一个简单的 java 开发编译环境

    目前 Java 语言的版本很多,除了常用的 Java 8,有一些遗留项目可能使用了 Java 7,也可能有一些比较新的的项目使用了 Java 10 以上的版本.如果想切换自己本地的 Java 开发环境 ...

  4. Js中函数声明和函数表达式的区别

    先看以下几段烧脑的代码: f();//=>? var f = function () { console.log("var"); } function f() { conso ...

  5. Socket accept 简要分析

    accept 用于从指定套接字的连接队列中取出第一个连接,并返回一个新的套接字用于与客户端进行通信,示例代码如下 #include <sys/types.h> /* See NOTES * ...

  6. select模型(二 改进服务端)

    一. int select(int fds,fd_set *readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout) ...

  7. mysql中delete from t1 where id = 10加锁状况叙述

    在Next_Key Lock算法中,不仅仅锁定住所找到的索引,而且还锁定住这些索引覆盖的范围.因此在这个范围内的插入都是不允许的.这样就避免了在这个范围内插入数据导致的幻读问题. delete fro ...

  8. Python_科学计算库

    说明:若没有训练级联表,则需要相关级联表才能实现功能 文字识别 # -*- coding: utf-8 -*- """ 简介:用样本训练数据,再识别 "&quo ...

  9. 美团 Java 面试 154 道题分享!

    Java集合22题 ArrayList 和 Vector 的区别. 说说 ArrayList,Vector, LinkedList 的存储性能和特性. 快速失败 (fail-fast) 和安全失败 ( ...

  10. Qt For MacOs环境搭建

    使用VMWARE关于macos镜像搭建,参考https://blog.csdn.net/u011415782/article/details/78505422 关于darwin8.5.5 来安装vmt ...