
一、开篇当字符串变成“代码执行指令”如果你写过 Web 框架如 Django/Flask你一定遇到过这样的场景前端传来的 URL 是/user/get_name后端需要根据字符串get_name去调用对应的方法。如果不反射你只能写一堆if-elif堪称“面向胶水编程”。而 Python 的反射四兄弟就是专门用来解决这种“运行时才知道叫什么名字”的终极利器。二、基础篇四兄弟各司其职class User: def __init__(self, name): self.name name u User(小明) # 1. hasattr探测有没有 print(hasattr(u, name)) # True print(hasattr(u, age)) # False # 2. getattr获取值带默认值保命 age getattr(u, age, 18) # 没有 age 属性返回默认 18不报错 # 3. setattr设置属性没有就新建 setattr(u, job, 程序员) # 4. delattr删除属性 delattr(u, job)三、进阶扩展①getattr的“默认值”妙用——告别 Try-Except在解析复杂的 JSON 数据时经常有字段缺失。用getattr配合默认值代码瞬间清爽# 不优雅的写法 try: username user_obj.name except AttributeError: username 匿名用户 # 优雅的反射写法 username getattr(user_obj, name, 匿名用户)四、进阶扩展②字符串变函数——动态调用方法框架核心getattr不仅能取数据还能取方法拿到方法后加上括号()即可执行。class Calculator: def add(self, a, b): return a b calc Calculator() method_name add # 假设这个字符串是从配置文件或网络请求中读到的 if hasattr(calc, method_name): func getattr(calc, method_name) result func(10, 20) # 动态调用成功输出 30这就是Flask 路由和命令行工具如 Click底层实现的核心原理——通过字符串映射到函数执行。五、进阶扩展③深入setattr与__dict__的爱恨纠葛当我们执行setattr(u, name, 新名字)时本质上是在修改u.__dict__这个字典。print(u.__dict__) # {name: 小明} setattr(u, age, 30) print(u.__dict__) # {name: 小明, age: 30}注意大坑如果类定义了__slots__则没有__dict__此时setattr会走“描述符协议”Descriptor Protocol。这也是property的底层实现机制有兴趣的读者可以深挖一下__get__和__set__魔法方法。六、进阶扩展④安全红线——绝对禁止用户输入直接反射这是绝大多数新手会踩的致命漏洞user_input input(输入你想调用的属性) # 恶意用户输入 __class__ print(getattr(u, user_input))如果用户输入__class__就能拿到类的元数据输入__bases__可以拿到父类甚至可以通过链式反射进入os模块执行系统命令最佳实践白名单校验ALLOWED_ATTRS [name, age, get_info] if user_input in ALLOWED_ATTRS: print(getattr(u, user_input)) else: print(非法访问) 总结反射给了 Python 程序“灵魂出窍”的能力让我们可以像操作字典一样操作对象。用好它可以写出高度抽象的框架滥用它则会写出难以维护的“魔法代码”。记住反射是银弹但只打怪兽不打队友。