练习要求

  • 写一个矩形类,默认有宽和高两个属性。
  • 如果为一个叫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 += 1
t = 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 = value
t = 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 called
print(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!

python魔法方法和普通方法_Python魔法方法之属性访问详解!相关推荐

  1. 技术图文:Python魔法方法之属性访问详解

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

  2. python爬虫多线程是什么意思_python爬虫中多线程的使用详解

    queue介绍 queue是python的标准库,俗称队列.可以直接import引用,在python2.x中,模块名为Queue.python3直接queue即可 在python中,多个线程之间的数据 ...

  3. python源程序文件的扩展名_python程序文件扩展名知识点详解

    python程序文件的扩展名称是什么 python程序的扩展名有.py..pyc..pyo和.pyd..py是源文件,.pyc是源文件编译后的文件,.pyo是源文件优化编译后的文件,.pyd是其他语言 ...

  4. python分析方向的第三方库_Python标准库与第三方库详解

    干货大礼包!21天带你轻松学Python(文末领取更多福利) 点击查看课程视频地址 本课程来自于千锋教育在阿里云开发者社区学习中心上线课程<Python入门2020最新大课>,主讲人姜伟. ...

  5. python中values是什么意思_Python values()与itervalues()的用法详解

    dict 对象有一个 values() 方法,这个方法把dict转换成一个包含所有value的list,这样,我们迭代的就是 dict的每一个 value: d = { 'Adam': 95, 'Li ...

  6. python selenium 等待页面加载_python selenium 三种等待方式详解(实战常用)

    引言: 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面加载太慢造成了元素不可见,那就必须要加等待了,等待元素可见再继续运行程序: 注:当使用该放发的时 ...

  7. python数据清理的实践总结_python 数据的清理行为实例详解

    python 数据的清理行为实例详解 数据清洗主要是指填充缺失数据,消除噪声数据等操作,主要还是通过分析"脏数据"产生的原因和存在形式,利用现有的数据挖掘手段去清洗"脏数 ...

  8. python标准类型内建模块_Python内建模块struct实例详解

    本文研究的主要是Python内建模块struct的相关内容,具体如下. Python中变量的类型只有列表.元祖.字典.集合等高级抽象类型,并没有像c中定义了位.字节.整型等底层初级类型.因为Pytho ...

  9. 基于python的随机森林回归实现_PYTHON | 随机森林实战(代码+详解)

    大家好,我是菜鸟君,之前跟大家聊过R语言的随机森林建模,指路 R语言 | 随机森林建模实战(代码+详解),作为刚过完1024节日的码农算法工程师来说,怎么可能只会用一种语言呢?今天就来说说Python ...

  10. python for i in range(len())_Python for i in range ()用法详解

    for i in range ()作用: range()是一个函数, for i in range () 就是给i赋值: 比如 for i in range (1,3): 就是把1,2依次赋值给i r ...

最新文章

  1. SharpStrike:基于C#实现的后渗透漏洞利用工具
  2. 关于丢番图方程x^2-dy^2=-1
  3. .NET 6新特性试用 | Nuget包验证
  4. jstack Dump日志文件中的线程状态
  5. linux comsol命令,如何从命令行运行 COMSOL Multiphysics®
  6. md5解密 python_python写一个md5解密器示例
  7. 基于python微信群聊机器人开题报告
  8. java计算机毕业设计西藏民族大学论文管理系统源程序+mysql+系统+lw文档+远程调试
  9. 三次方程求根公式例子
  10. python两两组合_python – 一个列表中的两个组合列表
  11. 整理--linux设备驱动模型
  12. python基础训练—数字
  13. Arduino实验三:继电器实验
  14. PNAS:睡眠的fMRI频谱特征
  15. kali中如何更新python_怎么在线更新kali linux
  16. 如何看待阿里云要做“全球产业AI的拓荒者”?
  17. 孙子兵法(Unicode有的所有字都已录入,还有它字疑为误传)
  18. Magic Leap开发指南(3)-- 将你的Cube投递给其他用户
  19. webrtc笔记(5): 基于kurento media server的多人视频聊天示例
  20. React Redux 进阶: Hooks 版本用法 Custom Context 局部 Store 实践

热门文章

  1. Only a type can be imported. xxxx resolves to a package
  2. selenium+python学习总结-mac
  3. edge浏览器识别ip地址为手机号的解决办法
  4. 如何将vs2015中的英文注释改为中文
  5. 关于HTML5画布canvas的功能
  6. 医疗信息季节:在医疗行业未来的变化(继续前传)
  7. 结对项目-使用计算器的设计和介绍
  8. 到底绿茶能不能减肥瘦小肚子? - 生活至上,美容至尚!
  9. sparkr基本操作1
  10. iOS 评论APP撰写评论