vars() 不能直接用于任意对象,因为它仅封装 object.__dict__,而只有具备 dict 属性的实例(如未使用 slots 或 frozen dataclass 的普通类实例)才支持;否则抛 TypeError。

vars() 为什么不能直接用在任意对象上
vars() 实际上只是 object.__dict__ 的封装,它只对自带 __dict__ 的实例有效。比如自定义类没禁用字典、也没用 __slots__,那 vars(obj) 才能返回属性字典;但遇到 namedtuple、dataclass(frozen=True)、内置类型(如 list、int)或用了 __slots__ 的类,就会直接抛 TypeError: vars() argument must have __dict__。
常见错误现象:vars(some_dataclass_instance) 报错,或者返回空字典,其实是因为该实例没有动态属性存储空间。
- 检查对象是否支持:
hasattr(obj, '__dict__')或getattr(obj, '__dict__', None) is not None - 如果类定义里写了
__slots__ = ['a', 'b'],就别指望vars()能读出属性 —— 它压根不走__dict__ - 对
dataclass,优先用asdict()(需导入from dataclasses import asdict),而不是vars()
vars() 和 __dict__ 的行为差异在哪
表面上 vars(obj) 等价于 obj.__dict__,但关键区别在于:前者会校验参数类型,后者是直接访问属性。也就是说,vars(123) 报错,而 (123).__dict__ 是 AttributeError(因为根本没这个属性),错误类型和提示信息都不同。
使用场景上,如果你写的是通用工具函数,想“尽力而为”地提取属性,建议先 try/except 包一层 vars(),失败后再 fallback 到其他方式(比如 __dataclass_fields__ 或 __slots__ 遍历)。
立即学习“Python免费学习笔记(深入)”;
-
vars()更安全:它至少保证返回的是一个dict(成功时),不会返回None或其他类型 -
obj.__dict__更快但更脆,绕过类型检查,适合已知对象结构的内部逻辑 - 注意:
vars()不会递归展开嵌套对象,它只取当前层的实例属性
如何安全地把任意对象转成字典(不止靠 vars)
真正在项目里做“对象转字典”,往往得组合多种策略。比如 Django model 实例、Pydantic 模型、普通 class、attrs 类,每种都有自己的序列化入口。硬套 vars() 很容易漏字段或崩掉。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
实操建议:按优先级链式尝试,避免一上来就报错中断:
- 如果是
dataclass实例:用asdict(obj)(支持嵌套和 frozen) - 如果是 Pydantic v2 的
BaseModel:用obj.model_dump(),不是dict()或vars() - 如果是带
__dict__的普通实例:先vars(obj),再手动过滤掉方法、下划线开头的私有属性(lambda k: not k.startswith('_')) - 如果对象有
__slots__:用{k: getattr(obj, k) for k in obj.__slots__}(前提是所有 slot 都已赋值)
示例片段(轻量 fallback):
def to_dict(obj):
try:
return vars(obj)
except TypeError:
if hasattr(obj, '__slots__'):
return {k: getattr(obj, k) for k in obj.__slots__}
elif hasattr(obj, '__dataclass_fields__'):
from dataclasses import asdict
return asdict(obj)
else:
raise ValueError(f"Cannot convert {type(obj).__name__} to dict")
vars() 返回的字典包含哪些内容
vars() 只返回实例属性(instance attributes),不包括类属性(class attributes)、方法、property 计算属性,也不触发 __get__ 协议。这意味着即使你定义了 @property,它也不会出现在 vars() 结果里。
容易被忽略的点:如果属性是通过 __setattr__ 动态添加、但没真正写入 __dict__(比如某些 ORM 或代理类做了拦截),vars() 也看不到。
- 类属性如
MyClass.version = "1.0"不会被vars(instance)捕获 -
@property字段需要显式调用getattr(obj, 'prop_name')才能取到值 - 如果对象重载了
__getattr__,vars()依然只看__dict__,不会触发该方法
真正要“完整反映对象状态”,必须结合类型特征设计逻辑,不能只依赖 vars() 这个窄口径接口。

















