引言
在上一篇《一文速通Python魔法方法》中,我们掌握了__init__
、__str__
等基础魔法方法。今天,老唐将带您学习“高阶魔法”,这里不仅有:
__getattr__
实现动态方法生成__call__
让实例变身函数__enter__/__exit__
构建智能管家更有来自Flask、PyTorch等顶级项目的魔法方法实现解析!
__call__
实现装饰器类场景:需要保存状态的装饰器
class CountCalls:
def __init__(self, func):
self.func = func
self.calls = 0
def __call__(self, *args, **kwargs):
self.calls += 1
print(f"已执行 {self.calls} 次")
return self.func(*args, **kwargs)
@CountCalls
def say_hello():
print("hello!")
say_hello() # 输出:已执行 1 次 \n hello!
say_hello() # 输出:已执行 2 次 \n hello!
__getattr__
实现动态代理场景:REST API客户端动态生成方法
class APIWrapper:
def __getattr__(self, name):
def wrapper(*args):
print(f"调用API方法: {name} 参数: {args}")
# 实际发送HTTP请求...
return wrapper
api = APIWrapper()
api.get_users(1, 20) # 输出:调用API方法: get_users 参数: (1, 20)
__enter__
/__exit__
实现智能锁class TimedLock:
def __enter__(self):
self.start = time.time()
print("获取锁")
def __exit__(self, exc_type, exc_val, exc_tb):
print(f"释放锁,耗时: {time.time()-self.start:.2f}s")
with TimedLock():
time.sleep(1)
# 输出:获取锁 \n 释放锁,耗时: 1.00s
@app.route
(__call__
的经典应用)Flask用__call__
让应用实例本身成为WSGI可调用对象:
class Flask:
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
__tablename__
)class User(db.Model):
__tablename__ = 'users' # 魔法属性控制表名
id = db.Column(db.Integer, primary_key=True)
import numpy as np
a = np.array([1,2,3])
b = np.array([4,5,6])
print(a + b) # 底层调用`__add__`魔法方法
Tensor
类型(多重魔法方法)x = torch.tensor([1.0])
y = torch.tensor([2.0])
print(x + y) # 通过`__add__`实现
print(x[0]) # 通过`__getitem__`实现
class SmartDict(dict):
def __missing__(self, key):
print(f"键 {key} 不存在,已设置默认值")
self[key] = None
return None
def __setitem__(self, key, value):
print(f"设置键值: {key} => {value}")
super().__setitem__(key, value)
def __contains__(self, key):
print("安全检查触发")
return super().__contains__(key)
d = SmartDict()
print(d["name"]) # 触发__missing__
d["age"] = 25 # 触发__setitem__
print("name" in d) # 触发__contains__
输出结果:
键 name 不存在,已设置默认值
None
设置键值: age => 25
安全检查触发
True
__eq__
时务必重载__hash__
# 低效写法(每次循环调用__getitem__)
for i in range(len(obj)):
print(obj[i])
# 高效写法
for item in obj: # 依赖__iter__
print(item)
__iter__
和__next__
__enter__
和__exit__
try:
from collections.abc import MutableMapping
except ImportError:
from collections import MutableMapping # Py2兼容
# 错误示范:破坏性运算符重载
class Vector:
def __add__(self, other):
self.x += other.x # 修改了自身!
return self
__iter__
, __getitem__
实现惰性查询)__getitem__
支持复杂索引)__init__
等样板代码)__contains__
实现智能检测)魔法方法的精妙之处在于:
obj[key]
代替obj.get(key)
)唐叔说:“真正Pythonic的代码不是写得像Python,而是让Python写得像你的领域语言”。魔法方法正是实现这一目标的终极武器。
(完)