Python 魔术方法是什么
魔术方法就是前后都有双下划线的方法,例如:
1 | __str__ |
它们不是让你直接调用的,而是让 Python 在某些语法场景下自动调用。
例如:
1 | len(obj) |
背后其实会调用:
1 | obj.__len__() |
1. __new__
作用
__new__ 负责“创建对象”。
对象创建过程分两步:
1 | obj = 类名() |
实际顺序是:
1 | __new__ # 先创建对象 |
示例
1 | ''' |
输出:
1 | __new__:创建对象 |
注意
__new__ 必须返回对象实例,否则 __init__ 不会正常执行。
1 | class A: |
输出:
1 | 创建失败 |
示例:单例
1 | ''' |
注意:
- 必须返回实例,否则对象创建失败
- 比
__init__更底层
2. __str__
作用
__str__ 用来定义对象给“用户”看的字符串。
触发方式
1 | print(obj) |
示例
1 | ''' |
输出:
1 | 姓名:Tom,年龄:18 |
3. __repr__
作用
__repr__ 用来定义对象给“开发者”看的字符串,主要用于调试。
触发方式
1 | repr(obj) |
或者在交互式环境中直接输入对象:
1 | p |
示例
1 | class Person: |
输出:
1 | Person(name='Tom', age=18) |
str 和 __repr__的区别
1 | ''' |
输出:
1 | 我是 Tom |
区别:
| 方法 | 面向对象 | 典型用途 |
|---|---|---|
__str__ |
普通用户 | 打印展示 |
__repr__ |
开发者 | 调试、日志 |
只写 __repr__ |
str(obj) 也会用它 |
常见 |
只写 __str__ |
repr(obj) 不会用它 |
调试信息仍然不清晰 |
4. __len__
作用
定义对象的长度。
触发方式
1 | len(obj) |
示例
1 | ''' |
输出:
1 | 3 |
注意
__len__ 必须返回整数。
5. __getitem__
作用
让对象支持中括号取值。
触发方式
1 | obj[key] |
示例:像字典一样取值
1 | ''' |
输出:
1 | zack |
6. __setitem__
作用
让对象支持中括号赋值。
触发方式
1 | obj[key] = value |
示例
1 | ''' |
7. __delitem__
作用
让对象支持中括号删除。
触发方式
1 | del obj[key] |
示例
1 | ''' |
8. __contains__
作用
让对象支持 in 判断。
触发方式
1 | key in obj |
示例
1 | ''' |
输出:
1 | True |
9. __call__
作用
让对象像函数一样被调用。
触发方式
1 | obj() |
示例
1 | class Add: |
输出:
1 | 8 |
使用场景
常用于:
1 | 模型对象() |
例如:
1 | class Greeter: |
10. __iter__
作用
让对象可以被 for 循环遍历。
触发方式
1 | for x in obj: |
实际会先调用:
1 | iter(obj) |
也就是:
1 | obj.__iter__() |
11. __next__
作用
定义每次迭代返回什么值。
触发方式
1 | next(obj) |
示例:自定义计数器
1 | ''' |
重点
迭代器必须满足:
1 | __iter__ 返回自己 |
12. __enter__
作用
进入 with 语句时执行。
触发方式
1 | with obj as x: |
会调用:
1 | obj.__enter__() |
13. __exit__
作用
离开 with 语句时执行。
即使 with 里面发生异常,也会执行。
示例
1 | class MyContext: |
输出:
1 | 进入 with |
RAII自动加解锁
为什么要用 with + 锁
普通写法(容易出问题):
1 | lock.acquire() |
问题:
- 容易忘记释放锁
- 出异常时容易死锁
👉 用 with 可以自动管理
基础示例:用 with 实现加锁/解锁
1 | import threading |
使用方式
1 | lock = MyLock() |
输出:
1 | 加锁 |
关键点解释
enter
1 | def __enter__(self): |
👉 进入 with 时执行
👉 用来“申请资源”(加锁、打开文件、连接数据库)
exit
1 | def __exit__(self, exc_type, exc_val, exc_tb): |
👉 离开 with 时执行
👉 无论是否异常都会执行
1 | lock = MyLock() |
综合案例:模拟一个字典类
1 | class MyDict: |
输出大致是:
1 | 正在设置 name = Tom |
总结
| 魔术方法 | 对应语法 | 作用 |
|---|---|---|
__new__ |
Class() |
创建对象 |
__str__ |
print(obj) / str(obj) |
面向用户的字符串 |
__repr__ |
repr(obj) |
面向开发者的字符串 |
__len__ |
len(obj) |
返回长度 |
__getitem__ |
obj[key] |
读取元素 |
__setitem__ |
obj[key] = value |
设置元素 |
__delitem__ |
del obj[key] |
删除元素 |
__contains__ |
key in obj |
判断是否包含 |
__call__ |
obj() |
对象像函数一样调用 |
__iter__ |
iter(obj) / for |
返回迭代器 |
__next__ |
next(obj) |
返回下一个元素 |
__enter__ |
with obj |
进入上下文 |
__exit__ |
with 结束 |
退出上下文,释放资源 |