太精彩了!欢迎来到 【跟着AI学Python】Python进阶:元编程与动态特性!🎉今天我们将探索 Python 最“魔法”的一面——让代码在运行时自我修改、自我生成!
💡 元编程(Metaprogramming):编写能操作程序的程序 —— 动态创建类、修改函数、拦截属性访问,甚至实现领域特定语言(DSL)!
🎯 今日目标
✅ 理解 type() 动态创建类✅ 掌握 装饰器的高级用法(修改函数行为)✅ 使用 __getattr__ / __setattr__ 拦截属性访问✅ 实现一个简单的 SQL 风格 DSL✅ 了解 元类(metaclass) 的基本概念
📘 一、核心概念速览
🔑 Python 一切皆对象:
- 函数是
function 的实例→ 所以我们可以动态构造它们!
🔧 二、实践 1:用 type() 动态创建类
场景:根据配置文件生成银行账户类型
# 动态创建一个新账户类型def init_method(self, owner): self.owner = owner self.balance = 0.0def deposit_method(self, amount): self.balance += amount print(f"💰 {self.owner} 存入 {amount}")# 动态创建类PremiumAccount = type( "PremiumAccount", # 类名 (object,), # 基类元组 { # 类字典(方法和属性) "__init__": init_method, "deposit": deposit_method, "interest_rate": 0.05 # 类属性 })# 使用acc = PremiumAccount("Alice")acc.deposit(1000)print(acc.interest_rate) # 0.05
✅ 优势:无需写死类定义,可从数据库/JSON 生成类!
🔧 三、实践 2:用装饰器修改函数行为
场景:为所有银行操作自动添加日志和事务
from functools import wrapsimport loggingdef bank_operation(func): """装饰器:为银行操作添加日志和异常处理""" @wraps(func) def wrapper(self, *args, **kwargs): operation = func.__name__ try: logging.info(f"开始操作: {operation} on {self.owner}") result = func(self, *args, **kwargs) logging.info(f"操作成功: {operation}") return result except Exception as e: logging.error(f"操作失败: {operation} - {e}") raise return wrapper# 应用到类方法class BankAccount: def __init__(self, owner): self.owner = owner self.balance = 0 @bank_operation def deposit(self, amount): if amount <= 0: raise ValueError("金额必须 > 0") self.balance += amount
✨ 效果:调用 acc.deposit(100) 会自动记录日志,无需修改业务逻辑!
🔧 四、实践 3:用 __getattr__ 实现智能代理
场景:实现一个“万能”账户代理,支持动态方法
class AccountProxy: def __init__(self, account): self._account = account def __getattr__(self, name): """当属性不存在时调用""" if name.startswith("quick_"): # 动态生成快捷方法 action = name[6:] # 去掉 "quick_" if action == "deposit_100": return lambda: self._account.deposit(100) elif action == "withdraw_50": return lambda: self._account.withdraw(50) raise AttributeError(f"'{self.__class__.__name__}' 无 '{name}' 属性")# 使用real_acc = BankAccount("Bob")proxy = AccountProxy(real_acc)proxy.quick_deposit_100() # 自动调用 deposit(100)print(proxy._account.balance) # 100.0
🔍 应用:
- API 客户端(
client.get_user() → 动态生成)
🔧 五、实践 4:实现 SQL 风格 DSL(领域特定语言)
目标:写出类似 User.select().where(age=30).all() 的查询
class QueryBuilder: def __init__(self, model_class): self.model = model_class self.conditions = {} def where(self, **kwargs): """添加查询条件""" self.conditions.update(kwargs) return self # 支持链式调用 def all(self): """执行查询(模拟)""" # 这里应连接数据库,我们模拟返回 mock_db = [ {"id": 1, "name": "Alice", "age": 30}, {"id": 2, "name": "Bob", "age": 25} ] result = [] for record in mock_db: match = True for key, value in self.conditions.items(): if record.get(key) != value: match = False break if match: result.append(record) return resultclass ModelMeta(type): """元类:为模型类自动添加 QueryBuilder""" def __new__(cls, name, bases, attrs): new_class = super().__new__(cls, name, bases, attrs) if name != "Model": # 不应用于基类 new_class.objects = QueryBuilder(new_class) return new_classclass Model(metaclass=ModelMeta): """模型基类""" passclass User(Model): pass# 使用 DSLusers = User.objects.where(age=30).all()print(users) # [{'id': 1, 'name': 'Alice', 'age': 30}]
🌟 这就是 Django ORM 的简化版!
🔍 六、元类(Metaclass)深入理解
元类是什么?
# 默认情况下,类由 type 创建classMyClass: pass# 等价于MyClass = type("MyClass", (), {})
自定义元类:强制编码规范
class StrictMeta(type): def __new__(cls, name, bases, attrs): # 检查所有方法是否包含文档字符串 for key, value in attrs.items(): if callable(value) and not value.__doc__: raise TypeError(f"方法 {key} 必须有文档字符串!") return super().__new__(cls, name, bases, attrs)class Bank(metaclass=StrictMeta): def transfer(self, amount): """转账功能""" # ← 必须有 docstring! pass
⚠️ 警告:元类是“深水区”,99% 场景不需要!优先考虑装饰器或 __init_subclass__。
📝 小结:元编程使用原则
“元编程不是炫技,而是解决特定问题的利器”
✅ 何时使用:
❌ 避免滥用:
🎉 恭喜完成元编程进阶!你已掌握 Python 最强大的动态特性,可以像框架作者一样思考!
继续加油,你的代码正在变得越来越魔力十足!✨