用 dir() 最直接,它返回对象所有可见名称;想只看实例自己定义的属性则查 __dict__,但需先判断是否存在。

怎么快速列出对象所有属性?用 dir() 最直接
dir() 是最常用、最轻量的方式,它返回一个字符串列表,包含对象“可见”的所有名称(包括方法、属性、继承来的成员)。它不区分来源,也不管是不是真的可访问——比如私有属性 _name 会出来,但 __private 可能被隐藏(取决于实现)。
常见错误现象:dir(obj) 显示一堆以双下划线开头结尾的方法(如 __init__),误以为这些都是“有用属性”;其实多数是协议方法,和业务属性无关。
- 适合调试时快速探查:比如
dir(request)看 Flask 的请求对象有哪些字段 - 对内置类型(
str、list)有效,但返回大量底层方法,需人工过滤 - 对自定义类实例,会包含从父类继承的属性,不一定都在当前实例上真实存在
- 不保证顺序,不同 Python 版本可能略有差异
想只看实例自己定义的属性?优先查 __dict__
__dict__ 是实例自己的属性字典,只存显式赋值过的实例变量(不包括类属性、方法、描述符、@property),所以更“干净”。但它不是所有对象都有——比如用了 __slots__ 的类、内置类型(int、list)或 C 扩展对象,会直接报 AttributeError: '__dict__' attribute not found。
使用场景:你刚初始化了一个对象,想确认哪些字段被 self.xxx = ... 赋过值,且确定它没开 __slots__。
立即学习“Python免费学习笔记(深入)”;
- 检查前先判断:
hasattr(obj, '__dict__'),避免炸掉 -
obj.__dict__.keys()比dir(obj)少很多噪音,适合做属性白名单校验 - 注意:类属性(如
class A: x = 1)不会出现在实例的__dict__里 - 如果类用了
__slots__ = ['name', 'age'],那实例没有__dict__,只能靠dir()或手动查 slots 列表
dir() 和 __dict__ 结果对不上?这很常见,原因有几个
这不是 bug,是设计使然。两者定位完全不同:dir() 是“你能点出来的所有名字”,__dict__ 是“你亲手塞进这个实例字典里的键”。中间隔着继承链、描述符、@property、__getattr__ 等机制。
典型例子:一个类有 @property 方法 full_name,dir(obj) 会列出它,但 obj.__dict__ 里找不到——因为它是动态计算的,不占实例存储空间。
-
__dict__不包含任何通过描述符(@property、classmethod、staticmethod)暴露的成员 - 如果类定义了
__getattr__,dir()可能把它“猜”进去(靠试探性调用),但__dict__完全无视 - 元类或动态添加的属性(如
setattr(obj, 'tmp', 42))会同时出现在两者中(前提是没开__slots__) - 对同一对象反复调用
dir(),结果可能变化(比如运行时注入了新方法)
真要可靠获取“可用属性值”?别只靠一个函数
单独依赖 dir() 或 __dict__ 都容易漏或错。实际写工具函数(比如序列化、debug 打印)时,得组合判断。
例如想安全地 dump 实例字段:
def safe_attrs(obj):
attrs = {}
if hasattr(obj, '__dict__'):
attrs.update(obj.__dict__)
# 补上 property 和显式定义的类属性(按需)
for name in dir(obj):
if not name.startswith('_') and not callable(getattr(obj, name, None)):
attrs.setdefault(name, getattr(obj, name))
return attrs
- 永远先
hasattr再取__dict__,否则遇到__slots__类就崩 -
callable()过滤掉方法,但注意:@property不 callable,却要保留 - 不要假设
dir()返回的每个名字都能getattr()成功——有些只是“存在声明”,访问时才抛错 - 如果对象来自第三方库(如 Pydantic
BaseModel、SQLAlchemy 模型),它们往往重写了__dir__或__dict__,得看文档
复杂点在于:Python 的属性访问是分层的,而 dir() 和 <strong>dict</strong> 各自只揭开了其中一层。不结合上下文(类定义、是否用了 slots、有没有自定义 <strong>getattr</strong>),光看输出列表根本没法判断某个名字到底代表什么。








