背景

今天在B站学习“零基础入门学习 Python”中的第45节“魔法方法:属性访问”,这也是我们组织的 Python基础刻意练习活动 的学习任务,其中有这样的一个题目。

练习要求

  • 写一个矩形类,默认有宽和高两个属性。
  • 如果为一个叫square的属性赋值赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。

技术分析

我们先来看看有关于属性的四个魔法方法:

  • __getattr__(self, name): 定义当用户试图获取一个不存在的属性时的行为。
  • __getattribute__(self, name):定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)。
  • __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__,但实际情况并非如此,为什么呢?

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

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

② 接着,去t.__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__

以上介绍完毕,那么 __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 = 1# __setattr__del t.x# __delattr__

对了,再补充一点哈!

class Test:def __init__(self):self.count = 0def __setattr__(self, name, value):print('__setattr__')self.count += 1t = Test()# AttributeError: 'Test' object has no attribute 'count'

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

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

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

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

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

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

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

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

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

class Test:def __setattr__(self, name, value):self.name = valuet = Test()
t.x = 'lsgo'# RecursionError: maximum recursion depth exceeded

当我们给 t.x 赋值时,调用了 __setattr__()方法,进入该方法。该方法中,又来了一次赋值(self.name = value),又会去调用 __setattr__() 方法,持续这个死循环。

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

class Test:def __setattr__(self, name, value):print('__setattr__() been called')super().__setattr__(name, value)t = Test()
t.x = 'lsgo'# __setattr__() been calledprint(t.x)# lsgo

代码实现

上面详细介绍了关于属性的四个魔法方法,下面我们来看实现要求的具体代码:

class Rectangle:def __init__(self, width=0, height=0):self.width = widthself.height = heightdef __setattr__(self, key, value):if key == 'square':self.width = valueself.height = valueelse:super().__setattr__(key, value)def getArea(self):return float(self.width) * float(self.height)r = Rectangle(4, 5)
print(r.getArea())  # 20.0
r.square = 10
print(r.__dict__)  # {'width': 10, 'height': 10}
print(r.getArea())  # 100.0

总结

魔法方法是 Python 面向对象编程中最核心的内容,需要花费一定的精力才能将其掌握,参加 Python基础刻意练习的小伙伴们加油! See You!


参考文献

  • https://www.bilibili.com/video/av4050443/?p=46
  • https://www.runoob.com/python3/python3-tutorial.html
  • https://www.cnblogs.com/Jimmy1988/p/6804095.html

相关图文

  • 资料分享:数学建模资料分享 – 图论部分
  • 资料分享:数学建模资料分享 – 神经网络部分
  • 如何利用 C# 实现 K 最邻近算法?
  • 如何利用 C# 实现 K-D Tree 结构?
  • 如何利用 C# + KDTree 实现 K 最邻近算法?
  • 如何利用 C# 对神经网络模型进行抽象?
  • 如何利用 C# 实现神经网络的感知器模型?
  • 如何利用 C# 实现 Delta 学习规则?
  • 如何利用 C# 实现 误差反向传播 学习规则?
  • 如何利用 C# 爬取带 Token 验证的网站数据?
  • 如何利用 C# 向 Access 数据库插入大量数据?
  • 如何利用 C# + Python 破解猫眼电影的反爬虫机制?

技术图文:Python魔法方法之属性访问详解相关推荐

  1. python魔法方法和普通方法_Python魔法方法之属性访问详解!

    练习要求: 写一个矩形类,默认有宽和高两个属性. 如果为一个叫square的属性赋值赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长. 技术分析 我们先来看看有关于属性的四个 ...

  2. 饥荒机器人怎么解锁_饥荒全人物解锁方法以及属性技能详解

    饥荒的原版游戏共有十余种人物供玩家们选择,每个角色属性技能不同,解锁条件也不同.下面小编就带来了饥荒全人物解锁方法及属性技能详解,一起来看看哪些角色适合玩耍吧. 第 5 页 机器人 机器人 英文名称: ...

  3. python入门——P45魔法方法:属性访问

    下面是通过property访问属性的用法 class C():def __init__(self, size=10):self.size = sizedef getsize(self):return ...

  4. Python 魔法方法与属性

    python的魔法方法很奇特,有些地方也称他为特殊方法.其结构是由两个下划线("_"开始中间名称最后以两个下划线("_")结束的特殊指定方法,这有点类似java ...

  5. python super()方法的作用_详解python的super()的作用和原理

    Python中对象方法的定义很怪异,第一个参数一般都命名为self(相当于其它语言的this),用于传递对象本身,而在调用的时候则不必显式传递,系统会自动传递.uz0免费资源网 今天我们介绍的主角是s ...

  6. python cmp方法_python cmp函数详解

    cmp( x, y):比较2个对象,前者小于后者返回-1,相等则返回0,大于后者返回1. Python的cmp比较函数比较原理 Python的cmp函数可以比较同类型之间,或者不同数据类型之间.然后根 ...

  7. python 魔法方法常用_Python魔法方法指南

    有很多人说学习Python基础之后不知道干什么,不管你是从w3c还是从廖雪峰的教程学习的,这些教程都有一个特点:只能引你快速入门,但是有关于Python的很多基础内容这些教程中都没介绍,而这些你没学习 ...

  8. python魔法方法长文详解

    python魔法方法详解 1. 什么是魔法方法 魔法方式(Magic methods)是python的内置函数,一般以双下划线开头和结尾,比如__add__,__new__等.每个魔法方法都有对应的一 ...

  9. Python 魔法方法详解

    什么是Python魔法方法 魔法方法就如同它的名字一样神奇,总能在你需要的时候为你提供某种方法来让你的想法实现.魔法方法是指Python内部已经包含的,被双下划线所包围的方法,这些方法在进行特定的操作 ...

最新文章

  1. 用buildout来构建python项目
  2. Linux Shell 1/dev/null 21 含义
  3. ASP.NET 实践:写入 Cookie
  4. 前端学习(159):meta
  5. 基于android新闻阅读器,Readian News是一款基于Android和网络的新闻阅读器,可让您掌控一切...
  6. 盐池元宵转九曲【山乡元夕】
  7. maven依赖冲突解决_Maven依赖树–解决冲突
  8. 宿舍小助手之个人分析NABCD
  9. 机组0:为什么补码比原码多一个-128清晰解释
  10. 为什么要学好数据结构和算法
  11. “2020 博客之星”年度总评选 TOP 200 名单已出,速来认领!
  12. 微软2017年预科生计划在线编程笔试第二场-#1498 : Diligent Robots
  13. E4A 易安卓一些常见的小问题
  14. excel表格生成图片的方式
  15. 手动删除病毒经历【usgop.exe】
  16. js判断扑克牌同花顺
  17. 【知识图谱】知识图谱的本质是什么?
  18. 51单片机:设计电子密码锁
  19. 11. 盛最多水的容器(java实现)--2种解法(左右夹逼,暴力)LeetCode
  20. 1083: 阶乘尾数零的个数

热门文章

  1. 计算机二级周小丹,亲爱的设计丨周小丹:始终认真,才能保持“天真”
  2. 可以左右移动多选下拉列表的javaScipt(可以兼容IE和firefox)
  3. 中小企业低成本快速建站的秘诀——模板建站
  4. [20170914]tnsnames.ora的管理.txt
  5. PostgreSQL SQL 语言:并行查询
  6. namenode如何存储复本?
  7. java试用(1)hello world
  8. 自动生成HTML的一段程序
  9. 解析腾讯行政区划API接口数据
  10. SpringBoot使用Socket向前端推送消息