Python-面向对象-魔法方法(未完待续)
目录
一、属性相关的魔法方法
(1) __getattribute__
(2)__getattr__
(3)__setattr__
(4)__delattr__
二,容器魔法方法
(1)__len__ ,__getitem__, __setitem__, __delItem__
(2) __iter__, __next__
一、属性相关的魔法方法
1.1 类别
- __getattr__(self, name)
- __setattr__(self, name)
- __getattribute__(self, name)
- __delattr__(self, name)
- __dir__(self, name)
1.2 属性访问顺序
1. 调用__getattribute__
2. 调用数据描述符 (同时具备三个魔术方法的描述符)
3. 调用当前对象的所属成员
4. 调用类的所属成员
5. 调用非数据描述符(没有同时具备三个魔术方法的类,他们两者的区别就是访问顺序的高低)
6. 调用父类的所属成员
7. 调用__getattr__
(1) __getattribute__
触发时机: 只要访问属性就会被触发,不管对象的属性存在与否。 作用: 在给予对象数据时可以对对象的属性进行处理。 参数:self:当前对象, item:接受访问对象成员的字符串。 返回值:有,不设定则返回None。 注意事项:在当前魔法方法中禁止使用 self.属性 的方式访问属性,否则会造成递归重复,必须借助object.__getattribute__来获取对象成员。
执行代码如下图所示:
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加方法# 添加魔术方法def __getattribute__(self, item):print("属性已被处理")return f"{self.name}先生" # __getattribute__返回的数据才是现在能访问得到的值def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls)# 访问对象的名称
print(ls.name)
运行代码会报错:
File "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py", line 20, in __getattribute__return f"{self.name}先生" # __getattribute__返回的数据才是现在能访问得到的值^^^^^^^^^[Previous line repeated 994 more times]File "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py", line 19, in __getattribute__print("属性已被处理")
RecursionError: maximum recursion depth exceeded while calling a Python object进程已结束,退出代码1
报错的原因是因为在 return f"{self.name}先生" 这行代码中也调用了对象的属性,而一旦调用属性就会执行 __getattribute__ ,之后又会调用return,触发了递归操作,造成大量的重复使得报错。
为了避免这种情况的发生,需要在调用对象属性的时候使用函数最底层的object来进行访问:
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加方法# 添加魔术方法def __getattribute__(self, item): # item接受的时访问属性的名称字符串而已print("属性已被处理") # 在这里使用最底层object来进行获取result = object.__getattribute__(self, item) # 此时就会将获取到的"name"赋值给result进行使用了# 隐藏用户名(让中间的名字为'*')return result[0] + "*" +result[-1] # __getattribute__返回的数据才是现在能访问得到的值def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls)# 访问对象的名称
print(ls.name)
运行代码:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
<__main__.Human object at 0x000001FDE5C71D10>
属性已被处理
李*四进程已结束,退出代码0
(2)__getattr__
1. 触发时机:访问不存在的对象成员的时候自动触发
2. 作用: 防止访问不存在成员的时候报错,为不存在的成员定义值
3. 参数:一个self接收当前对象,第二个参数接收访问成员的名称字符串
4. 返回值:有即返回,无就None
5. 注意事项:木有
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加魔法方法def __getattr__(self, item):print("__getattr__被触发")def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls)# 访问对象的名称
print(ls.name)
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
<__main__.Human object at 0x0000021A3DE51C10>
李四进程已结束,退出代码0
可见__getattr__没有触发:
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加魔法方法def __getattr__(self, item):print("__getattr__被触发")def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls)# 访问对象的名称
print(ls.name2)
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
<__main__.Human object at 0x0000028688631C50>
__getattr__被触发
None进程已结束,退出代码0
此时因为没有name2这个属性,所以__getattr__触发,同时__getattr__可以通过返回值给未定义的属性赋值
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加魔法方法def __getattr__(self, item):print("__getattr__被触发")return "定义值"def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls)# 访问对象的名称
print(ls.name2)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
<__main__.Human object at 0x00000234B3711C90>
__getattr__被触发
定义值进程已结束,退出代码0
(3)__setattr__
触发时机:添加对象属性,或者修改对象属性的时候触发
作用:可以限制或者管理对象成员的添加与修改操作
参数:一个self接收当前对象,第二个参数接收访问成员的名称字符串
返回值:可有可无
注意事项:在当前魔术方法中禁止使用:当前对象.成员名 = 值的方式.会触发递归操作
class Human:# 添加属性def __init__(self, name, sex, age):self.name = nameself.sex = sexself.age = age# 添加魔法方法def __setattr__(self, key, value): # 一旦发生属性设置,就会被调用!print("__setattr__被调用!")def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18)
print(ls.name)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
__setattr__被调用!
__setattr__被调用!
__setattr__被调用!
Traceback (most recent call last):File "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py", line 22, in <module>print(ls.name)^^^^^^^
AttributeError: 'Human' object has no attribute 'name'进程已结束,退出代码1
从上图中我们可以看出报错的结果是因为 "Hunman" 未能定义属性 "name" ,这是因为在初始化函数__init__中,定义 self.属性 也算是设置属性,所以都调用了__setattr__,才会出现三个 __setattr__函数被调用,这也导致了__init__函数设置属性时被拦截,所以未定义"name"等属性
__setattr__(self, key, value)中三个参数中key实例对象的属性的名称,一般称为attrname,value时实例对象的真实值
class Human:# 添加属性def __init__(self, name, sex, age, wife):self.name = nameself.sex = sexself.age = ageself.wife = wife# 添加魔法方法def __setattr__(self, key, value): # 一旦发生属性设置,就会被调用!if key == "wife":super().__setattr__("wife", "张三") # 使输入的wife不管为什么,最后输出都为"张三"# self.__dict__[wife] = "张三" # 这个语句与上面的语句等效else:super().__setattr__(key, value) # 不是wife属性的直接继承__setattr__,保持参数不变# self.__dict__[key] = value # 同理def eat(self):print("吃饭")def drink(self):print("喝酒")# 实例化对象
ls = Human("李四", "男", 18, "张三")
print("修改前:", ls.__dict__)
print("前:", ls.wife)
# 修改参数
ls = Human("李老四", "男", 68, "王五")
print("修改后:", ls.__dict__)
print("后:", ls.wife)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
修改前: {'name': '李四', 'sex': '男', 'age': 18, 'wife': '张三'}
前: 张三
修改后: {'name': '李老四', 'sex': '男', 'age': 68, 'wife': '张三'}
后: 张三进程已结束,退出代码0
从上图中我们可以看出,__setattr__可以在修改参数时进行自定义操作,比如上图,即使我们修改了实例化对象的参数wife,欲使其变成 "王五",但是通过结果我们发现,wife并没有发生变化,这就是__setattr__的作用。
(4)__delattr__
通过下面的例子我们可以看出,__delattr__和之前学习的一样,都可以对删除操作进行拦截并且自重构删除操作,但是还是要注意不能使用 self.属性
class Human:# 添加属性def __init__(self, name, sex, age, wife):self.name = nameself.sex = sexself.age = ageself.wife = wife# 添加魔法方法def __setattr__(self, key, value): # 一旦发生属性设置,就会被调用!if key == "wife":super().__setattr__("wife", "张三")else:super().__setattr__(key, value)def __delattr__(self, item): # 自定义删除方式if item == "wife": # 只要删除"wife",就会打印下面,而且不会删除"wife"print("你小子敢删我老婆,我砍死你!")else:del self.__dict__[item] # 删除别的属性就执行删除操作# 实例化对象
ls = Human("李四", "男", 18, "张三")
print("删除前:", ls.__dict__)
# 删除参数
del ls.name
print("删除后:", ls.__dict__)
print("只要没动我老婆就好")
del ls.wife
print("删除后:", ls.__dict__)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe "H:\PycharmProjects\pythonProject1\Python学习\magic\magic. __getattribute__.py"
删除前: {'name': '李四', 'sex': '男', 'age': 18, 'wife': '张三'}
删除后: {'sex': '男', 'age': 18, 'wife': '张三'}
只要没动我老婆就好
你小子敢删我老婆,我砍死你!
删除后: {'sex': '男', 'age': 18, 'wife': '张三'}进程已结束,退出代码0
源视频:https://www.bilibili.com/video/BV11K4y1C7TB/?spm_id_from=333.337.search-card.all.click&vd_source=62c17a2b0f761d6d01138d268a216191
二,容器魔法方法
(1)__len__ ,__getitem__, __setitem__, __delItem__
可以定义一个容器的类,但是使用这个类所创建的实例对象,是不能直接被访问即操作的,如下图所示:
class DictDemo:def __init__(self, key, value):self.Dict = {}self.Dict[key] = value# def __getitem__(self, item):# print("getitem is working")# return 1## def __setitem__(self, key, value):# print("__setitem__ is working")# self.Dict[key] = value## def __delitem__(self, key):# print("__delitem__ is working")# del self.Dict[key]## def __len__(self):# print("__len__ is working")# return len(self.Dict)d = DictDemo("one", 1)print(d.Dict)
print(d.Dict["one"])
# print(len(d))
print(d["one"])
# d["two"] = 2
# print(d.Dict)
# del d["two"]
# print(d.Dict)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
{'one': 1}
1
Traceback (most recent call last):File "H:\PycharmProjects\pythonProject1\Temporaryfiles.py", line 28, in <module>print(d["one"])~^^^^^^^
TypeError: 'DictDemo' object is not subscriptable进程已结束,退出代码1
定义了一个字典,没法直接进行访问,所以现在可以引入__getitem__来进行获取,__setitem__来对实例化的对象进行设置,__delitem__来对实例对象进行删除,__len__可以获得实例对象的长度如下图
class DictDemo:def __init__(self, key, value):self.Dict = {}self.Dict[key] = valuedef __getitem__(self, item):print("getitem is working")return self.Dict[item]def __setitem__(self, key, value):print("__setitem__ is working")self.Dict[key] = valuedef __delitem__(self, key):print("__delitem__ is working")del self.Dict[key]def __len__(self):print("__len__ is working")return len(self.Dict)d = DictDemo("one", 1)print(d.Dict)
print(d.Dict["one"])
print(len(d))print(d["one"])
d["two"] = 2
print(d.Dict)
print(d["two"])
print(len(d))del d["two"]
print(d.Dict)
print(len(d))运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
{'one': 1}
1
__len__ is working
1
getitem is working
1
__setitem__ is working
{'one': 1, 'two': 2}
getitem is working
2
__len__ is working
2
__delitem__ is working
{'one': 1}
__len__ is working
1进程已结束,退出代码0
只要实例对象发生获取操作就会触发__getitem__ ,从而被__getitem__拦截,其他函数同理!
(2) __iter__, __next__
在实例对象进行迭代操作时触发的魔法方法,__iter__的作用时是将实例对象转变为一个迭代对象,不然的话不能进行__next__操作,__next__就相当于while循环了,注意一定要终止循环,不然被会一直循环下去
time = 0class Double:def __init__(self, start, stop):self.value = start - 1self.stop = stopdef __iter__(self):return selfdef __next__(self):global timeif self.value == self.stop:raise StopIterationself.value += 1time += 1return self.value * 2d = Double(1, 5)
for i in d:print(i, end=" ")print("迭代次数:", time)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
2 4 6 8 10 迭代次数: 5进程已结束,退出代码0
三、成员关系检测魔法方法
这个魔法方法主要是__cintains__(self, item), 由 in 或 not in 触发,主要用来检测成员是否在实例对象中。这里还要介绍一个叫做代偿的概念,代偿就是在查找不到__cintains__(self, item)时,就会找其他函数来代替。
class C:def __init__(self, data):self.data = data# 优先级:1def __contains__(self, item): # 这里的item就是下面的 3 和6print(item)print("__contains__ is working")return item in self.data# 优先级:2def __iter__(self):# print("__iter__ is working")print("开始", end="->")self.i = 0return selfdef __next__(self):# print("__next__ is working")print("正在查找", end="->")if self.i == len(self.data):raise StopIterationitem = self.data[self.i]self.i += 1return item # 因为这里返回的是一个非零的参数,所以print的结果就是True# 优先级:3def __getitem__(self, item):print("正在查找", end="->")return self.data[item] # 因为这里返回的是一个非零的参数,所以print的结果就是Truec = C([1, 2, 3, 4, 5])
print(3 in c)
print(6 in c)运行结果(优先级1时):
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
3
__contains__ is working
True
6
__contains__ is working
False进程已结束,退出代码0运行结果(优先级2时):
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
开始->正在查找->正在查找->正在查找->True
开始->正在查找->正在查找->正在查找->正在查找->正在查找->正在查找->False进程已结束,退出代码0运行结果(优先级3时):
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
正在查找->正在查找->正在查找->True
正在查找->正在查找->正在查找->正在查找->正在查找->正在查找->False进程已结束,退出代码0
四、bool类型魔法方法及算数魔法方法
bool类型魔法方法由bool函数所触发,同样具有优先级
class D:# 优先级:1def __bool__(self):print("__bool__ is working")return True# # 优先级:2# def __init__(self, data):# self.data = data## def __len__(self):# print("__len__ is working")# return len(self.data) # 因为返回的是一个非零的参数,所以print的结果就是True# d = D([1, 2, 3])
# print(bool(d))
# print(bool([])) # 这里由于是空集,所以返回就是Falsea = D()
print(bool(a))
print(bool([]))运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
__bool__ is working
True
False进程已结束,退出代码0
算法魔法方法简单,在比较时就会调用
class S(str):def __lt__(self, other):return len(self) < len(other)def __gt__(self, other):return len(self) > len(other)def __eq__(self, other):return len(self) == len(other)s1 = S("FishC")
s2 = S("fishc")print(s1 < s2)
print(s1 > s2)
print(s1 == s2)print(s1 != s2) # 注意:这里运行的结果是True,这是由于没有定义__ne__(self, other),所以还是使用的普通的比较方式,
# 即比较字符串的编码值,所以就不相等,同理的还有<=(__le__),>=(__ge__),如果不对其进行定义,那么他还是会执行本来的方法运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
False
False
True
True进程已结束,退出代码0
__call__魔法方法就相当于将实例对象看作为一个函数,在进行这个操作时就会调用这个魔法方法,可以与之前的闭包函数相比较。
class Power:def __init__(self, exp):self.exp = expdef __call__(self, base):return base ** self.exps1 = Power(2)
square = s1(3) # 这里将实例对象当作函数使用,所以就调用了魔法方法!
s2 = Power(3)
cube = s2(3)
print("平方:", square, "立方:", cube)运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
平方: 9 立方: 27进程已结束,退出代码0
__str__与__repr__魔法方法
# # 函数讲解
# print(str("Python")) # str函数是参数转为为字符串对象
# print(repr("Python")) # repr函数是将对象转换为程序可执行的字符串,面向程序
# print(eval("1 + 2")) # eval函数可以将参数去引号后执行
# print(eval(repr("Python"))) # 可以看出,eval是repr函数的反函数,从结果上可以看出。# 魔法方法
""" Note:这两个魔法方法必须是返回字符串的类型,而且__repr__可以对__str__进行代偿 """"""function1"""
class C:def __init__(self, data):self.data = datadef __str__(self):return f"data = {self.data}"def __repr__(self):return f"C({self.data})"def __add__(self, other):self.data += otherc = C(250)
print(c) # 优先级问题,__str__优先级要比__repr__高,所以会出现打印参数不同的问题运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
data = 250进程已结束,退出代码0"""function2"""
class C:def __init__(self, data):self.data = data# def __str__(self):# return f"data = {self.data}"def __repr__(self):return f"C({self.data})"def __add__(self, other):self.data += otherc = C(250)
print(c) # 优先级问题,__str__优先级要比__repr__高,所以会出现打印参数不同的问题运行结果:
H:\PycharmProjects\pythonProject1\venv\Scripts\python.exe H:\PycharmProjects\pythonProject1\Temporaryfiles.py
C(250)进程已结束,退出代码0
Python-面向对象-魔法方法(未完待续)相关推荐
- python线程和进程-未完待续
python线程和进程-未完待续 环境变量 0. 概念 1. 并行/并发 并行 并发 并行与并发的关系 2.进程/线程 基本概念 线程 多线程 队列 互斥锁/线程共享 阻塞 锁 条件锁 进程 多进程 ...
- 线下实体店铺会员引流的四种方法-未完待续
2022年,因为疫情的影响以及电商平台的持续发力,线下门店消费者到店流量明显减少,线下实体门店如何才能通过更低的成本更高的效率进行线下引流呢?这是当下零售企业需要重视的问题. 今天博阳为您介绍4种会员 ...
- # Python基础笔记(未完待续)
写在前面:小白闲来无事,参考小甲鱼视频重温Python,所及笔记,仅供参考.第一次写长笔记,格式较乱,请谅解 一.数据类型 1.输入路径 >>>print("D:\thre ...
- python与统计学(未完待续)
感谢阅读 前言 基础知识 AB测试 描述统计和推断统计 概率分布 简介 The binomial distribution The Poisson distribution The normal di ...
- 观察多个线程同时运行|| 查看进程线程的方法——未完待续
观察多个线程同时运行:主要是理解,交替执行,谁先谁后,不由我们控制 查看进程线程的方法
- Python基础:内置异常(未完待续)
Python基础:内置异常(未完待续) 参考文章: (1)Python基础:内置异常(未完待续) (2)https://www.cnblogs.com/luo630/p/9176768.html 备忘 ...
- 用python做归结演绎推理_Python中惯用的一些操作总结(未完待续)
Python中常用的一些操作总结(未完待续) 写在前面的话 其实也没有什么可以写的,或者说完全没有价值.因为你只要动一动手指就可以在Google上找到我要写的这些东西.只是我还不习惯好久没有碰我的bl ...
- python爬虫requests源码链家_python爬虫——爬取链家房价信息(未完待续)
爬取链家房价信息(未完待续) items.py # -*- coding: utf-8 -*- # Define here the models for your scraped items # # ...
- python __reduce__魔法方法_Python魔法方法指南
(译)Python魔法方法指南 简介 本指南归纳于我的几个月的博客,主题是 魔法方法 . 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法" ...
最新文章
- redis在PHP中的基本使用案例
- Apache - AH00526 – server.crt
- opencv 计时 帧率
- valgrind-3.11.0 交叉编译
- 鉴别一个人是否 js 入门的标准竟然是?!
- python executemany执行延迟_运维架构师-Python 自动化运维开发-031
- C#LeetCode刷题之#707-设计链表(Design Linked List)
- 听说你想爬点壁(mei)纸图
- 零基础入门│带你理解Kubernetes
- 判断一个字符串是否是回文_Python
- 字符串,字节,二进制转换
- 「雅礼集训 2018 Day2」农民
- 推荐一些程序猿学习的网站
- grub4dos引导启动linux,Grub4Dos 手动引导指令
- 纯java写2D格斗游戏(一)——界面背景设置及人物的简单设置
- Typecho 将评论头像从Gravatar改成显示QQ头像
- python十二星座符号_12种编程语言类比12星座女
- 利用Kalibr标定Camera-IMU外参
- 输入五个城市从小到大排序-c语言
- 消息队列系列之分布式消息队列Kafka