You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取Python对象的所有实例(非类)属性列表?

这确实是Python里一个容易踩坑的问题——获取仅属于实例的属性(排除类属性)本来挺直观,但__slots__的各种特殊规则把事情搞复杂了。我来一步步拆解问题,然后给出一个靠谱的解决方案:

先说说常规情况和__slots__的坑

  • 对于没有定义__slots__的类,直接用o.__dict__或者vars(o)就能拿到所有实例属性的字典,键就是属性名——这是大家最常用的方式。
  • 但如果类定义了__slots__,事情就变了:
    1. 实例默认不会有__dict__,除非__slots__里显式包含__dict__这个字段。就算有__dict__,里面也只存那些不在slot里的属性。
    2. __slots__不一定是列表或元组,甚至可以是一次性迭代器!比如:
      class Foo: __slots__ = (f"slot_{n}" for n in range(3))
      
      这个类确实会创建slot_0、slot_1、slot_2三个slot,但Foo.__slots__返回的是已经耗尽的迭代器,根本拿不到实际的slot名称。
    3. slot还能从父类继承,只遍历当前类的__slots__肯定不全。

聊聊dir()的优缺点

dir(o)几乎能列出所有来自__dict__和slot的属性,但有两个明显的问题:

  • 小问题:它没法区分哪些slot是已经赋值过的,哪些是空的。这个可以通过getattr(o, attr)捕获AttributeError来过滤,但有点繁琐。
  • 大问题:它会把类属性(比如类方法、类变量)混进来,而且没有简单可靠的方式直接过滤掉——毕竟有些实例属性会覆盖类属性,不能粗暴地把所有类里有的属性都排除。

靠谱的解决方案:自定义函数

我写了一个函数,可以处理所有这些边缘情况,准确获取仅属于实例的属性:

def get_instance_attributes(o):
    instance_attrs = set()
    # 先处理__dict__里的属性(如果实例有__dict__)
    if hasattr(o, '__dict__'):
        instance_attrs.update(o.__dict__.keys())
    
    cls = type(o)
    # 遍历所有可能的属性(来自dir(o))
    for attr in dir(o):
        # 跳过已经在__dict__里的属性
        if attr in instance_attrs:
            continue
        
        try:
            # 直接获取实例属性,避免描述符干扰
            instance_value = object.__getattribute__(o, attr)
            # 检查这个属性是否是类层面定义的
            is_class_attr = False
            class_value = None
            for base in cls.__mro__:
                if attr in base.__dict__:
                    is_class_attr = True
                    class_value = object.__getattribute__(base, attr)
                    break
            
            if not is_class_attr:
                # 不是类属性,说明是已赋值的slot
                instance_attrs.add(attr)
            else:
                # 是类属性,但如果实例覆盖了它(引用不同),就算实例属性
                if instance_value is not class_value:
                    instance_attrs.add(attr)
        except AttributeError:
            # 未赋值的slot,直接跳过
            continue
    
    return sorted(instance_attrs)

函数逻辑说明:

  1. 先收集__dict__里的属性——这些肯定是实例独有的。
  2. 遍历dir(o)里的每个属性,跳过已经处理过的__dict__属性。
  3. 对每个属性,先判断是否是类层面定义的:
    • 如果不是类属性,那它只能是已赋值的slot,加入结果。
    • 如果是类属性,就对比实例属性和类属性的引用:如果引用不同,说明实例覆盖了这个类属性,属于实例属性;否则跳过。
  4. 捕获AttributeError,排除未赋值的slot。

测试几个场景:

  • 常规类:
    class Bar:
        x = 1
        def __init__(self):
            self.y = 2
    b = Bar()
    b.x = 3  # 覆盖类属性
    print(get_instance_attributes(b))  # 输出: ['x', 'y']
    
  • 带__slots__的类:
    class Foo:
        __slots__ = ('a', 'b')
        c = 3
    f = Foo()
    f.a = 1  # b未赋值
    print(get_instance_attributes(f))  # 输出: ['a']
    
  • 用迭代器定义__slots__的类:
    class Spam:
        __slots__ = (f"slot_{n}" for n in range(3))
        d = 4
    s = Spam()
    s.slot_0 = 0  # slot_1、slot_2未赋值
    print(get_instance_attributes(s))  # 输出: ['slot_0']
    

注意事项

唯一的小例外:如果实例赋值的属性值和类属性是同一个不可变对象(比如小整数、字符串),instance_value is class_value会返回True,这时候函数不会把它当成实例属性。但这种情况其实符合逻辑——因为实例并没有真正覆盖类属性,只是赋值了相同的不可变值,Python会复用这些对象的引用。如果确实需要把这种情况算进去,可以把is not改成!=,但要注意可变对象的场景(比如实例修改了类属性的可变对象,这时候不应该算实例属性)。

内容的提问来源于stack exchange,提问作者macdjord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 09:32:39