文章目录
- 魔术方法的简介
- 魔术方法的作用
- 魔术方法汇总
- 特殊属性
- `__init__`初始化
- `__new__` 构造方法
- `__str__` 对象描述
- `__repr__` 对象描述
- `__call__` 模糊对象和函数
- `__del__`析构方法
- `__len__` 长度计算
- `__boor__ `布尔转换
- `__enter__` 进入
- `__exit__` 退出
- 上下文管理器
- 成员属性相关
- `__getattribute__`
- `__getattr__`
- `__setattr__`
- `__delattr__`
- 示例代码
- 容器相关
- `__setitem__`
- `__getitem__`
- `__delitem__`
- `__contains__`
- 迭代器
- `__iter__`
- `__next__`
- 比较相关
- `__eq__`
- `__ne__`
- `__gt__`
- `__ge__`
- `__lt__`
- `__le__`
- 运算相关
- `__add__`
- `__sub__`
- `__mul__`
- `__truediv__`
魔术方法的简介
所谓
魔法方法
,它的官方的名字实际上叫special method
;是Python的一种高级语法,允许你在类中自定义函数,并绑定到类的特殊方法中。
无论是magic methods还是魔术方法,在官方文档中是没有出现过的,但这些名称都被广泛的使用着。
special method
的特点,就是method的名字前后都有两个下划线,所以这些方法也被称为dunder methods
。在我们平时写程序的时候,已经或多或少的接触过不少的魔术方法,比如:
__init__
、__str__
就很常见。这些方法在Python中扮演着重要的角色,允许开发者对Python内置的操作进行自定义,从而使对象的行为更加符合特定的需求。
魔术方法的作用
- 定制对象的行为: 通过重写魔术方法,可以
改变
对象对特定操作的响应,例如加法、减法、字符串表示、类型转换等。- 实现操作符重载: 允许改变对象在使用内置操作符时的行为,比如
+
、-
、*
等。- 提供内置函数的支持: 例如
len()
、iter()
等内置函数,在调用时会尝试查找对象对应的魔术方法。- 实现上下文管理: 如
__enter__
和__exit__
方法用于实现with
语句的上下文管理。- 简化对象的使用: 通过提供对常见任务(如迭代或比较)的内置支持,可以简化对象的使用。
魔术方法汇总
特殊属性
属性 | 含义 |
---|---|
__name__ | 类、函数、方法的名字 |
__module__ | 类定义所在的模块名 |
__class__ | 对象或类所属的类 |
__bases__ | 类的基类的元组 |
__doc__ | 类、函数的文档字符串,如果没有定义则为None |
__mro__ | 类的mro,class.mro()返回的结果保存在此 |
__dict__ | 类或者实例的属性,可写的字典 |
__dir__ | 返回类或者对象的所有成员名称列表,dir() 函数就是调用__dir__() ;如果提供__dir__() 则返回属性的列表,否则会尽量从__dict__ 属性中收集信息 |
__init__
初始化
- 作用说明: 构造函数,用于
初始化新创建的对象
,当创建对象时自动调用此方法- 应用场景: 文件的打开、数据的获取、
函数执行前的准备
- 触发时机: 使用类名创建对象时
class Cola():# 初始化方法def __init__(self):self.brand = 'Coca-Cola'# 成员方法def drink(self): print('Pepsi-Cola')# 实例化类就会自动触发执行__init__方法
cola = Cola()
print(cola.brand)# 构造函数
class Cola():# 初始化方法def __init__(self,brand,price):self.brand = brandself.price = price# 成员方法def drink(self):print('Pepsi-Cola')# 实例化类就会自动触发执行__init__方法
coke = CocaCola('Coca-Cola',4)
print(coke.brand)
print(coke.price)
__new__
构造方法
- 作用说明: 对象初始化时执行
__new__
,目的是为该对象分配内存空间- 应用场景: 设计模式中的单例模式
- 触发时机: 实例化对象时自动触发执行
(在 __init__ 之前执行)
- 方法参数: 1.cls接收当前类,2.其他参数根据初始化方法的参数进行决定
- 方法返回: 必须返回
object.__new__(cls)
进行对象的创建,没有返回值则实例化对象的结果为None
- 注意事项:
__new__
方法的参数和__init__
方法的参数要保持一致,除了第一个参数
# 定义一个人类
class Person():# 构造方法def __new__(cls, *args, **kwargs):print('触发了构造方法...')print(args)print(kwargs)# 如果在该方法中没有返回对象,则对象无法创建print(cls) # 将当前类传递进去return object.__new__(cls)# 初始化方法def __init__(self,name,age,sex):print('触发了初始化方法...')self.name = nameself.age = ageself.sex = sex# 析构方法def __del__(self):print('触发了析构方法...')pass# 实例化对象
zsf = Person('张三丰',180,'男')
print('张三丰:',zsf) # 对象创建成功
__str__
对象描述
- 作用说明:返回一个对象的非正式或可打印的字符串表示,可以自定义输出信息
- **应用场景:**当尝试将一个对象转换为字符串时
- **触发时机:**当使用
str()
函数时触发
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):# 返回一个简洁的、易于阅读的字符串return f"Person(name={self.name}, age={self.age})"# 创建一个Person对象
p = Person("Alice", 30)# 使用str函数或print函数时,会调用__str__方法
print(p) # 输出: Person(name=Alice, age=30)
print(str(p)) # 输出: Person(name=Alice, age=30)
__repr__
对象描述
- 作用说明: 返回一个对象的官方或无歧义的字符串表示
- 应用场景: 当尝试将一个对象转换为字符串时
- 触发时机: 在使用repr方法对当前对象进行转换时自动触发
- 注意事项: 正常情况下,如果没有
__str__
方法,该方法就会代替,也就是优先级低于__str__
class Person:def __init__(self, name, age):self.name = nameself.age = agedef __str__(self):# 返回一个简洁的、易于阅读的字符串return f"Person(name={self.name}, age={self.age})"def __repr__(self):# 返回一个精确的、官方的字符串表示return f"Person('{self.name}', {self.age})"# 创建一个Person对象
p = Person("Alice", 30)# 使用str函数或print函数时,会调用__str__方法
print(p) # 输出: Person(name=Alice, age=30)
print(str(p)) # 输出: Person(name=Alice, age=30)# 使用repr函数时,会调用__repr__方法
print(repr(p)) # 输出: Person('Alice', 30)
__call__
模糊对象和函数
- 作用说明: 允许对象像函数那样被调用
- 应用场景: 当尝试将一个对象当作函数调用
- 触发时机: 把对象当作函数直接调用时自动触发执行
# 定义一个人类
class Person():# 构造方法def __new__(cls, *args, **kwargs):print('触发了构造方法...')print(args)print(kwargs)# 如果在该方法中没有返回对象,则对象无法创建print(cls) # 将当前类传递进去return object.__new__(cls)# 初始化方法def __init__(self,name,age,sex):print('触发了初始化方法...')self.name = nameself.age = ageself.sex = sexdef __call__(self, *args, **kwargs):print('你把对象当成了函数使用...')# 实例化对象
zsf = Person('张三丰',180,'男')
print('张三丰:',zsf) # 对象创建成功zsf() # 如果没有__call__方法无法直接调用对象
__del__
析构方法
- 作用说明: 析构函数,用于清理资源
- 应用场景: 如在初始化方法中打开的文件、连接的资源,可以在析构中关闭
- 触发时机: 当前类实例化的对象被销毁时,自动触发执行
- 注意事项: 是对象被销毁时触发了这个方法,而不是这个方法销毁了对象
回收机制:
- 当程序执行完毕,内存中所有的资源都会被销毁释放
- 使用del关键字手动删除
- 对象没有被任何对象被引用时,会自动销毁
import timeclass writeLog():fileUrl = './' # 日志文件的路径fileName = time.strftime('%Y-%m-%d') + '.log' # 日志文件的名称# 初始化方法def __init__(self):# 打开文件self.fileObj = open(self.fileUrl + self.fileName, 'a+', encoding='utf-8')# 写日志的方法def log(self, s):print(f'假设把日志:{s}写入文件中')# 析构方法def __del__(self):print('析构方法触发执行,关闭打开的文件')# 在对象被销毁时,关闭初始化方法中打开的文件对象self.fileObj.close()log = writeLog()
log.log('今天天气很不好!')
del log # 如果此时销毁log对象,则先行触发析构方法,不会将日志写入文件
print('---注意销毁顺序') # 销毁顺序遵从回收机制
__len__
长度计算
- 作用说明: 定义了当使用
len()
函数获取对象长度时的行为。它应该返回一个整数,表示对象中的元素个数
- 应用场景: 当想要自定义的类能够支持
len()
函数,即能够获取对象中的元素个数时,需要实现__len__
方法- 触发时机: 当使用
len()
函数获取对象的长度时,__len__
方法会被触发
class CustomContainer:def __init__(self):self.items = []def add(self, item):self.items.append(item)def __len__(self):return len(self.items)# 使用 CustomContainer 类
container = CustomContainer()
container.add("apple")
container.add("banana")
container.add("cherry")length = len(container) # 触发 __len__ 方法
print(length) # 输出:3
__boor__
布尔转换
- 作用说明: 定义了当对象被用在需要布尔值的上下文中时的行为。它应该返回一个布尔值
True
或False
,表示对象的真值状态- 应用场景: 当想要自定义的类能够支持布尔上下文,比如
if语句
或while循环
的条件判断时,需要实现__bool__
方法。- 触发时机: 当对象被用在一个需要布尔值的上下文中时,比如在
if语句
的条件判断或作为循环的条件时,__bool__
方法会被触发;如果类中没有定义__bool__
方法,Python 会尝试调用__len__
方法,如果对象是可迭代的,并且长度为0,则返回False
,否则返回True
。
class Demo():listUrl = [1,3,5,7,9]# 可以代替对象使用len函数,并返回一个指定的整型def __len__(self):return len(self.listUrl)# 可以代替对象进行str或者print的字符串信息返回便于用户读取def __str__(self):return '<当前类实例对象的描述 str...>'# 同样可以代替对象进行描述,但存在的目的在于开发者调试def __repr__(self):return '<当前类实例对象的描述 repr...>'# 可以代替对象使用bool函数,并返回一个布尔类型def __bool__(self):return bool(self.listUrl)
---------------------------------------------------------------# 实例化对象
obj = Demo()# len
print(len(obj))
# str 如果没有该方法,则打印的是对象的内存地址
print(obj)
# repr 如果和str方法一起实现,则需要使用对象调用repr函数
print(obj.__repr__())
# bool
print(bool(obj))
---------------------------------------------------------------# str 和 repr 函数都可以把其他类型的值转为字符串
num = 521
print(str(num),type(str(num))) # 521 <class 'str'>
print(repr(num),type(repr(num))) # 521 <class 'str'># repr解析str类型的值时带了引号
s = '521'
print(str(s),type(str(s))) # 521 <class 'str'>
print(repr(s),type(repr(s))) # '521' <class 'str'>'''
str 和 repr 函数都能够把其他类型的数据转为字符串类型
str 函数会把对象转为更适合阅读的形式返回
repr函数会把对象转为解释器更容易读取的形式返回
如果数据对象并没有更明显的区别,str和repr的结果是一样的
'''
__enter__
进入
- 作用说明:
__enter__
方法定义了当使用with
语句时,会话管理器在块被初始创建时要产生的行为。它的返回值通常绑定到with
语句的目标或as
后面的变量上,以便在代码块中使用- 应用场景:
__enter__
方法通常与with
语句一起使用,用于定义上下文管理器的行为。它可以用于管理资源,如文件、网络连接、数据库连接等,确保在代码块执行完毕后资源能够被正确释放- 触发时机: 当
with
语句被执行时,首先会调用__enter__
方法。在__enter__
方法执行完毕后,代码块中的语句开始执行。无论代码块是否发生异常,当代码块执行完毕后,都会调用__exit__
方法
__exit__
退出
- 作用说明: 清理和释放在
__enter__
方法中获取的资源- 应用场景: 最常见的应用场景是资源管理,如文件、网络连接、数据库连接、锁等。
__exit__
方法确保这些资源在代码块执行完毕后能够被正确释放或关闭,防止资源泄露。- 触发时机: 当
with
语句块正常执行完毕或发送异常(无论异常有没有被捕捉)时
上下文管理器
class MyFileContext:def __enter__(self, filename):self.file = open(filename, 'r')return self.filedef __exit__(self, exc_type, exc_value, traceback):if self.file is not None:self.file.close()# 使用上下文管理器
with MyFileContext('example.txt') as file:content = file.read()print(content)
# 当 with 语句块执行完毕后,MyFileContext 的 __exit__ 方法会被调用,从而关闭文件
成员属性相关
__getattribute__
- 作用说明: 获取一个属性的值。如果属性不存在,并且没有定义
__getattr__
方法,则会引发AttributeError
异常- 应用场景: 这个方法提供了一种方式来拦截对属性访问的常规过程,允许你添加自定义的逻辑。
- 触发时机: 当访问对象成员时,自动触发,无论当前成员是否存在
- 注意事项: 在当前的魔术方法中,禁止使用对象.成员的方式进行成员访问,会触发递归,如果想要在当前魔术方法中访问对象的成员,必须使用
object.__getattribute__(self,item)
__getattr__
- 作用说明: 允许提供一个
默认的行为来处理访问不存在的属性
;防止访问不存在的成员时报错,也可以为不存在的成员进行赋值操作- 触发时机: 当访问对象中不存在的成员时,自动触发
- 注意事项: 会优先执行
__getattribute__
方法,不可在当前方法中访问这个不存在的成员,会触发递归
__setattr__
- 作用说明: 可以
限制或管理对象成员的添加和修改
操作- 触发时机: 当给对象的成员进行赋值操作时自动触发(添加、修改)
- 方法参数: 1.self接收当前对象、2.设置的成员名、3.设置的成员值
- 注意事项: 在当前的魔术方法中禁止给当前对象的成员直接进行赋值操作,会触发递归,如果想要给当前对象的成员赋值,必须使用
object.__setattr__(self,key,value)
__delattr__
- 作用说明: 可以
限制对象成员的删除,还可以删除不存在的成员时防止报错
- 触发时机: 当删除对象的成员时自动触发
- 方法参数: 1.self接收当前、2.item删除的成员名称
- 注意事项: 在当前的魔术方法中禁止给当前对象的成员直接进行赋值操作,会触发递归,如果想要删除当前对象的成员,必须使用
object.__delattr__(self,item)
示例代码
class Person():name = '名字'age = '年龄'sex = '性别'def __init__(self,name,age,sex):self.name = nameself.age = ageself.sex = sexdef say(self):print('聊聊人数,谈谈理想...')def sing(self):print('高歌一曲,豪放一生...')# 获取对象成员时自动触发def __getattribute__(self, item):print('__getattribute__访问属性:', item)# print(self.name) # 触发递归 重复执行996次 严谨使用self当前对象# 通过try来实现判断访问的属性是否存在try:# 在方法中使用object来获取属性值res = object.__getattribute__(self,item)return res[0] + '*' + res[-1]except:return False# 当访问不存在的成员时自动触发def __getattr__(self, item):print('__getattr__访问属性:',item)return False# 当给对象成员进行赋值时触发,该方法中如果没有给对象成员赋值,那么赋值失败def __setattr__(self, key, value):print('__setattr__访问属性:', self, key, value)object.__setattr__(self, key, value)# 当删除对象的成员时自动触发def __delattr__(self, item):print('__delattr__访问属性:',item)object.__delattr__(self,item)zsf = Person('张三疯',18,'男')
print(zsf.name)
print(zsf.abc)
zsf.abc = 'aabbcc'
print(zsf.abc)
del zsf.abc
print(zsf.abc)
容器相关
操作 | 魔术方法 | 意义 |
---|---|---|
a[key]=value | __setitem__(self, key, value) | 赋值操作 |
a[item] | __getitem__(self, item) | 索引操作 |
del a[key] | __delitem__(self, key) | 删除元素 |
len(a) | __len__(self) | 容器长度 |
in/not in | __contains__(self, item) | 是否包含某个元素 |
reversed() | __reversed__(self) | 反转容器 |
__setitem__
- 作用说明: 用于设置容器中指定索引位置的元素值
- 触发时机: 当使用索引操作符
[]
来设置容器中的元素时触发
class MyContainer:def __init__(self):self.items = []def __setitem__(self, index, value):self.items[index] = valuecontainer = MyContainer()
container[0] = 10 # 设置容器中索引为0的元素为10
print(container.items) # 输出:[10]
__getitem__
- 作用说明: 获取容器中指定索引位置的元素
- 触发时机: 当使用索引操作符
[]
来访问容器中的元素时
class MyContainer:def __init__(self):self.items = []def __getitem__(self, index):return self.items[index]container = MyContainer()
container.items.append(10)
print(container[0]) # 输出:10
__delitem__
- 作用说明: 删除容器中指定索引位置的元素
- 触发时机: 当使用 del 语句来删除容器中的元素时
class MyContainer:def __init__(self):self.items = []def __delitem__(self, index):del self.items[index]container = MyContainer()
container.items.append(10)
del container[0] # 删除容器中索引为0的元素
print(container.items) # 输出:[]
__contains__
- 作用说明: 检查容器中是否包含特定的元素
- 触发时机: 当使用
in
关键字来检查一个元素是否是容器的成员时
class MyContainer:def __init__(self):self.items = []def __contains__(self, item):return item in self.itemscontainer = MyContainer()
container.items.append(10)
print(10 in container) # 输出:True
迭代器
__iter__
- 作用说明: 返回一个迭代器对象,该对象定义了迭代过程中所需的
__next__
方法。当对类实例使用for...in
循环时,Python会首先调用__iter__
方法来获取迭代器,然后反复调用迭代器的__next__
方法来遍历元素- 应用场景: 当想要一个类的实例能够被迭代时,需要实现
__iter__
方法。
class MyIterator:def __init__(self, start, end):self.value = startself.end = enddef __iter__(self):return self # 返回迭代器本身def __next__(self):if self.value >= self.end:raise StopIterationcurrent_value = self.valueself.value += 1return current_valuemy_iter = MyIterator(0, 3)
for i in my_iter:print(i) # 输出 0, 1, 2
__next__
- 作用说明: 返回迭代器中的下一个元素。当迭代器中的所有元素都被访问过后,该方法应该抛出一个
StopIteration
异常来通知for...in
循环停止- 应用场景: 通常不直接在自定义类中实现,而是在实现
__iter__
方法的迭代器类中实现。这个方法用于返回迭代器的下一个元素。
class MyIterator:def __init__(self, start, end):self.value = startself.end = enddef __iter__(self):return self # 返回迭代器本身def __next__(self):if self.value >= self.end:raise StopIterationcurrent_value = self.valueself.value += 1return current_valuemy_iter = MyIterator(0, 3)
for i in my_iter:print(i) # 输出 0, 1, 2
比较相关
__eq__
- 作用说明: 定义了对象之间的相等比较逻辑。它应该返回一个布尔值,指示调用对象是否等于另一个对象。
- 应用场景: 当想自定义对象之间的等于(
==
)比较行为时,需要实现__eq__
方法。- 触发时机: 当使用等于运算符
==
比较两个对象时,__eq__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __gt__(self, other):return self.value > other.valuea = MyClass(5)
b = MyClass(3)
print(a > b) # 输出:True
__ne__
- 作用说明: 定义了对象之间的不等于比较逻辑。它应该返回一个布尔值,指示调用对象是否不等于另一个对象。
- 应用场景: 当想自定义对象之间的等于(
!=
)比较行为时,需要实现__ne__
方法。- 触发时机: 当使用不等于运算符
!=
比较两个对象时,__ne__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __ne__(self, other):return self.value != other.valuea = MyClass(5)
b = MyClass(3)
print(a != b) # 输出:True
__gt__
- 作用说明: 定义了对象之间的大于比较逻辑。它应该返回一个布尔值,指示调用对象是否大于另一个对象。
- 应用场景: 当想自定义对象之间的大于
>
比较行为时,需要实现__gt__
方法。- 触发时机: 当使用大于运算符
>
比较两个对象时,__gt__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __eq__(self, other):return self.value == other.valuea = MyClass(5)
b = MyClass(5)
print(a == b) # 输出:True
__ge__
- 作用说明: 定义了对象之间的大于等于比较逻辑。它应该返回一个布尔值,指示调用对象是否大于等于另一个对象。
- 应用场景: 当想自定义对象之间的大于等于
>=
比较行为时,需要实现__ge__
方法。- 触发时机: 当使用大于等于运算符
>=
比较两个对象时,__ge__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __ge__(self, other):return self.value >= other.valuea = MyClass(5)
b = MyClass(3)
print(a >= b) # 输出:True
__lt__
- 作用说明: 定义了对象之间的小于比较逻辑。它应该返回一个布尔值,指示调用对象是否小于另一个对象。
- 应用场景: 当想自定义对象之间的小于 < 比较行为时,需要实现
__lt__
方法。- 触发时机: 当使用小于运算符 < 比较两个对象时,
__lt__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __lt__(self, other):return self.value < other.valuea = MyClass(5)
b = MyClass(3)
print(a < b) # 输出:False
__le__
- 作用说明: 定义了对象之间的小于等于比较逻辑。它应该返回一个布尔值,指示调用对象是否小于等于另一个对象。
- 应用场景: 当想自定义对象之间的小于等于
<=
比较行为时,需要实现__le__
方法。- 触发时机: 当使用小于等于运算符
<=
比较两个对象时,__le__
方法会被触发。
class MyClass:def __init__(self, value):self.value = valuedef __le__(self, other):return self.value <= other.valuea = MyClass(5)
b = MyClass(5)
print(a <= b) # 输出:True
运算相关
运算符 | 魔术方法 | 意义 |
---|---|---|
+ | add | 加 |
- | sub | 减 |
* | mul | 乘 |
/ | truediv | 除 |
// | floordiv | 向下取整 |
% | mod | 取余 |
** | pow | 乘方 |
__add__
- 作用说明: 定义了对象之间的加法运算行为
- 应用场景: 当希望类的实例支持加法运算时,需要实现
__add__
方法- 触发时机: 当使用加法运算符
+
将两个对象相加时,__add__
方法会被触发
class MyNumber:def __init__(self, value):self.value = valuedef __add__(self, other):if isinstance(other, MyNumber):return MyNumber(self.value + other.value)else:raise TypeError("Unsupported operand type for +")a = MyNumber(5)
b = MyNumber(3)
c = a + b # 触发 __add__ 方法
print(c.value) # 输出:8
__sub__
- 作用说明: 定义了对象之间的减法运算行为
- 应用场景: 当希望类的实例支持减法运算时,需要实现
__sub__
方法- 触发时机: 当使用减法运算符
-
将两个对象相减时,__sub__
方法会被触发
class MyNumber:def __init__(self, value):self.value = valuedef __sub__(self, other):if isinstance(other, MyNumber):return MyNumber(self.value - other.value)else:raise TypeError("Unsupported operand type for -")a = MyNumber(5)
b = MyNumber(3)
c = a - b # 触发 __sub__ 方法
print(c.value) # 输出:2
__mul__
- 作用说明: 定义了对象之间的乘法运算行为
- 应用场景: 当希望类的实例支持乘法运算时,需要实现
__mul__
方法- 触发时机: 当使用乘法运算符
*
将两个对象相乘时,__mul__
方法会被触发
class MyNumber:def __init__(self, value):self.value = valuedef __mul__(self, other):if isinstance(other, MyNumber):return MyNumber(self.value * other.value)else:raise TypeError("Unsupported operand type for *")a = MyNumber(5)
b = MyNumber(3)
c = a * b # 触发 __mul__ 方法
print(c.value) # 输出:15
__truediv__
- 作用说明: 定义了对象之间的除法运算行为
- 应用场景: 当希望类的实例支持除法运算时,需要实现
__truediv__
方法- 触发时机: 当使用乘法运算符
/
将两个对象相除时,__truediv__
方法会被触发
class CustomNumber:def __init__(self, value):self.value = valuedef __truediv__(self, other):if isinstance(other, CustomNumber):return CustomNumber(self.value / other.value)else:raise TypeError("Unsupported operand type for /")a = CustomNumber(10)
b = CustomNumber(2)
c = a / b # 触发 __truediv__ 方法
print(c.value) # 输出:5.0