目录

一、属性相关的魔法方法

(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-面向对象-魔法方法(未完待续)相关推荐

  1. python线程和进程-未完待续

    python线程和进程-未完待续 环境变量 0. 概念 1. 并行/并发 并行 并发 并行与并发的关系 2.进程/线程 基本概念 线程 多线程 队列 互斥锁/线程共享 阻塞 锁 条件锁 进程 多进程 ...

  2. 线下实体店铺会员引流的四种方法-未完待续

    2022年,因为疫情的影响以及电商平台的持续发力,线下门店消费者到店流量明显减少,线下实体门店如何才能通过更低的成本更高的效率进行线下引流呢?这是当下零售企业需要重视的问题. 今天博阳为您介绍4种会员 ...

  3. # Python基础笔记(未完待续)

    写在前面:小白闲来无事,参考小甲鱼视频重温Python,所及笔记,仅供参考.第一次写长笔记,格式较乱,请谅解 一.数据类型 1.输入路径 >>>print("D:\thre ...

  4. python与统计学(未完待续)

    感谢阅读 前言 基础知识 AB测试 描述统计和推断统计 概率分布 简介 The binomial distribution The Poisson distribution The normal di ...

  5. 观察多个线程同时运行|| 查看进程线程的方法——未完待续

    观察多个线程同时运行:主要是理解,交替执行,谁先谁后,不由我们控制 查看进程线程的方法

  6. Python基础:内置异常(未完待续)

    Python基础:内置异常(未完待续) 参考文章: (1)Python基础:内置异常(未完待续) (2)https://www.cnblogs.com/luo630/p/9176768.html 备忘 ...

  7. 用python做归结演绎推理_Python中惯用的一些操作总结(未完待续)

    Python中常用的一些操作总结(未完待续) 写在前面的话 其实也没有什么可以写的,或者说完全没有价值.因为你只要动一动手指就可以在Google上找到我要写的这些东西.只是我还不习惯好久没有碰我的bl ...

  8. python爬虫requests源码链家_python爬虫——爬取链家房价信息(未完待续)

    爬取链家房价信息(未完待续) items.py # -*- coding: utf-8 -*- # Define here the models for your scraped items # # ...

  9. python __reduce__魔法方法_Python魔法方法指南

    (译)Python魔法方法指南 简介 本指南归纳于我的几个月的博客,主题是 魔法方法 . 什么是魔法方法呢?它们在面向对象的Python的处处皆是.它们是一些可以让你对类添加"魔法" ...

最新文章

  1. redis在PHP中的基本使用案例
  2. Apache - AH00526 – server.crt
  3. opencv 计时 帧率
  4. valgrind-3.11.0 交叉编译
  5. 鉴别一个人是否 js 入门的标准竟然是?!
  6. python executemany执行延迟_运维架构师-Python 自动化运维开发-031
  7. C#LeetCode刷题之#707-设计链表(Design Linked List)
  8. 听说你想爬点壁(mei)纸图
  9. 零基础入门│带你理解Kubernetes
  10. 判断一个字符串是否是回文_Python
  11. 字符串,字节,二进制转换
  12. 「雅礼集训 2018 Day2」农民
  13. 推荐一些程序猿学习的网站
  14. grub4dos引导启动linux,Grub4Dos 手动引导指令
  15. 纯java写2D格斗游戏(一)——界面背景设置及人物的简单设置
  16. Typecho 将评论头像从Gravatar改成显示QQ头像
  17. python十二星座符号_12种编程语言类比12星座女
  18. 利用Kalibr标定Camera-IMU外参
  19. 输入五个城市从小到大排序-c语言
  20. 消息队列系列之分布式消息队列Kafka

热门文章

  1. 【iOS】MVC模式
  2. (4)ESP32 Python 用OLED播放Bad Apple
  3. php rsa教程,PHP RSA加解密示例
  4. 【本科研究生】论文选题的 4 种方法和 5 条判断标准
  5. established 太多_如何解决线上大量的NON_ESTABLISHED?
  6. sklearn调库实现决策树算法
  7. FCC决定拍卖2.5GHz频谱关键频段 帮助推进5G发展
  8. java8之元空间(Metaspace)
  9. 【Java】数据存储单位换算关系
  10. Matlab二维绘图---plot函数详解