========================================
Python 反射与自省机制
========================================

一、什么是反射与自省
----------------------------------------

自省(Introspection)是指程序在运行时检查自身结构和状态的能能力。
反射(Reflection)则更进一步,允许程序在运行时动态地创建对象、调用方法和修改属性。

Python 提供了强大的自省和反射机制,这正是它被称为"可自省的"语言的原因。


二、type() 和 isinstance() — 运行时类型检查
----------------------------------------

# type() —— 获取对象的类型
class Animal:
pass

class Dog(Animal):
pass

dog = Dog()
print(type(dog)) #
print(type(dog) is Dog) # True(精确类型检查)
print(type(dog) is Animal) # False(type 不考虑继承)

# isinstance() —— 检查类型(考虑继承链)
print(isinstance(dog, Dog)) # True
print(isinstance(dog, Animal)) # True(Dog 继承自 Animal)
print(isinstance(dog, object)) # True(所有类都继承自 object)

# issubclass() —— 检查类的继承关系
print(issubclass(Dog, Animal)) # True
print(issubclass(Animal, object)) # True

# 实用示例:类型分发
def process_data(data):
"""根据不同类型执行不同处理"""
if isinstance(data, str):
return f"处理字符串: {data.upper()}"
elif isinstance(data, (int, float)):
return f"处理数字: {data * 2}"
elif isinstance(data, list):
return f"处理列表: 共 {len(data)} 个元素"
else:
return f"未知类型: {type(data).__name__}"


三、dir() 和 vars() — 属性自省
----------------------------------------

class Person:
"""人类——用于展示属性自省的示例类"""

species = "智人" # 类属性

def __init__(self, name: str, age: int):
self.name = name # 实例属性
self.age = age # 实例属性
self._secret = "隐藏属性" # 私有属性约定

def greet(self) -> str:
return f"你好,我是{self.name}"

p = Person("李四", 30)

# dir() —— 列出对象的所有属性和方法名
print(dir(p))
# 输出包含了 __init__, greet, name, age, _secret 等

# vars() —— 返回 __dict__(实例的属性字典)
print(vars(p))
# 输出: {'name': '李四', 'age': 30, '_secret': '隐藏属性'}

# 过滤出用户自定义的属性(非特殊方法)
user_attrs = {
k: v for k, v in vars(p).items()
if not k.startswith("_") # 排除私有属性
}
print(user_attrs) # {'name': '李四', 'age': 30}


四、getattr/setattr/hasattr/delattr — 属性反射
----------------------------------------

# hasattr() —— 检查对象是否有某个属性
print(hasattr(p, "name")) # True
print(hasattr(p, "address")) # False

# getattr() —— 获取属性(可提供默认值)
name = getattr(p, "name") # 李四
address = getattr(p, "address", "未知地址") # 默认值
method = getattr(p, "greet") # 获取方法对象
print(method()) # 你好,我是李四

# setattr() —— 动态设置属性
setattr(p, "age", 31)
setattr(p, "address", "北京市海淀区")
print(p.age) # 31

# delattr() —— 删除属性
if hasattr(p, "_secret"):
delattr(p, "_secret")

# 实用示例:动态配置对象
def apply_config(obj, config: dict) -> None:
"""将字典配置动态应用到对象"""
for key, value in config.items():
if hasattr(obj, key):
setattr(obj, key, value)
else:
print(f"警告: 对象没有属性 '{key}',已跳过")


五、inspect 模块 — 深入自省工具箱
----------------------------------------

import inspect
from typing import get_type_hints

class Calculator:
"""计算器类——用于展示 inspect 模块的用法"""

def __init__(self, precision: int = 2):
self.precision = precision

def add(self, a: float, b: float) -> float:
"""将两个数字相加并返回结果"""
return round(a + b, self.precision)

def subtract(self, a: float, b: float) -> float:
"""将两个数字相减"""
return round(a - b, self.precision)

calc = Calculator()

# getmembers() —— 获取对象的所有成员(带类型过滤)
methods = inspect.getmembers(calc, predicate=inspect.ismethod)
print(methods)
# 输出: [('add', ...), ('subtract', ...), ...]

# signature() —— 获取函数签名信息
sig = inspect.signature(calc.add)
print(sig) # (a: float, b: float) -> float
for name, param in sig.parameters.items():
print(f"参数: {name}, 类型: {param.annotation}, 默认值: {param.default}")

# getsource() —— 获取函数源代码
source = inspect.getsource(calc.add)
print(source)
# 输出 add 方法的完整源代码

# isfunction/isclass/ismethod —— 类型谓词
print(inspect.isfunction(calc.add)) # False(绑定方法)
print(inspect.ismethod(calc.add)) # True
print(inspect.isclass(Calculator)) # True
print(inspect.isfunction(Calculator.add)) # True(未绑定的函数)

# getfile() —— 获取定义对象的文件路径
print(inspect.getfile(Calculator))
# 输出当前文件的完整路径


六、__name__ 和 __qualname__ — 名称自省
----------------------------------------

class Outer:
"""外层类——展示 __name__ 和 __qualname__ 的区别"""

class Inner:
"""内层类"""

def method(self):
pass

def top_level_func():
pass

# __name__ —— 仅类名/函数名
print(Outer.__name__) # Outer
print(Outer.Inner.__name__) # Inner
print(top_level_func.__name__) # top_level_func

# __qualname__ —— 限定名(包含所在类的完整路径)
print(Outer.__qualname__) # Outer
print(Outer.Inner.__qualname__) # Outer.Inner
print(Outer.Inner.method.__qualname__) # Outer.Inner.method

# 应用场景:日志记录器自动推断名称
import logging
logger = logging.getLogger(__name__) # 自动使用模块名


七、函数注解运行时检查
----------------------------------------

from typing import get_type_hints, Annotated

def fetch_data(
url: str,
timeout: int = 30,
retry: bool = True
) -> dict[str, any]:
"""从指定 URL 获取数据"""
return {"status": "ok"}

# 获取所有类型注解
hints = get_type_hints(fetch_data)
print(hints)
# 输出: {'url': str, 'timeout': int, 'retry': bool, 'return': dict[str, any]}

# __annotations__ 原始字典
print(fetch_data.__annotations__)
# 输出: {'url': , 'timeout': , ...}

# 实用:基于注解的参数验证
def validate_call(func):
"""装饰器:运行时验证函数参数类型"""
from functools import wraps
hints = get_type_hints(func)

@wraps(func)
def wrapper(*args, **kwargs):
# 简单类型检查(实际应更完善)
for name, value in kwargs.items():
if name in hints and not isinstance(value, hints[name]):
raise TypeError(f"参数 {name} 应为 {hints[name]}, 实际为 {type(value)}")
return func(*args, **kwargs)
return wrapper


八、__dict__ 与 __slots__ — 属性存储
----------------------------------------

class WithDict:
"""使用 __dict__ 存储属性(默认方式)"""
def __init__(self):
self.x = 1
self.y = 2

class WithSlots:
"""使用 __slots__ 优化内存"""
__slots__ = ("x", "y") # 固定属性列表

def __init__(self):
self.x = 1
self.y = 2

d = WithDict()
s = WithSlots()

print(type(d.__dict__)) # —— 动态字典
print(d.__dict__) # {'x': 1, 'y': 2}

# WithSlots 没有 __dict__,属性存储在固定大小的数组中
# print(s.__dict__) # AttributeError: 'WithSlots' 没有 __dict__

# __slots__ 优势:内存更小,访问更快
# __slots__ 劣势:不能动态添加属性
# setattr(s, "z", 3) # AttributeError!

# 也可以混用 __slots__ 和 __dict__
class MixedSlots:
__slots__ = ("fixed",) # 快速访问固定属性
# 未在 __slots__ 中的属性仍然走 __dict__

def __init__(self):
self.fixed = "固定属性"
self.dynamic = "动态属性" # 存在 __dict__ 中
 

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐