You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

如何通过类方法静态获取类的所有实例属性(支持继承场景)

如何通过类方法静态获取类的所有实例属性(支持继承场景)

首先聊聊你遇到的问题:你想用类方法拿到类的实例属性(就是__init__里给self绑定的那些属性),还得支持继承。你之前试的__dir__()是实例方法,直接用类调用返回的是类本身的属性列表,不是实例的;而MyClass.__dict__是类的命名空间,存的是类方法、类属性这些,自然拿不到实例的属性。

下面给你两种实用的解决方案,你可以根据自己的场景选择:


方法一:创建临时实例(简单直接,适合无副作用的__init__

如果你的类的__init__方法只是单纯初始化属性,没有修改外部状态、发起请求这类副作用,那直接创建一个临时实例,然后取它的__dict__键列表就可以了,还能自动处理继承的属性:

class MyClass():
    def __init__(self, x = None, y = None):
        self.a = x
        self.b = y

    @classmethod
    def get_all_attr(cls):
        # 用默认参数创建临时实例
        temp_instance = cls()
        # 返回实例的属性列表,顺序和初始化顺序一致
        return list(temp_instance.__dict__.keys())

class MyChildClass(MyClass):  # 注意这里要继承MyClass,你原来的代码漏了
    def __init__(self, z = None, **kwargs):
        self.c = z
        super().__init__(**kwargs)

>>> MyClass.get_all_attr()
['a', 'b']
>>> MyChildClass.get_all_attr()
['c', 'a', 'b']

优点:代码简单,不需要额外依赖,自动处理继承关系;
缺点:如果__init__需要必填参数、或者有副作用,这种方法就不适用了。


方法二:解析__init__的源代码(无实例创建,适合有副作用的场景)

如果你的__init__有副作用,或者不想创建实例,那可以通过解析__init__方法的源代码,找出所有给self赋值的属性,还能遍历父类的__init__来收集继承的属性:

import inspect
import ast

class MyClass():
    def __init__(self, x = None, y = None):
        self.a = x
        self.b = y

    @classmethod
    def get_all_attr(cls):
        attrs = set()
        # 遍历当前类及其所有父类(按照方法解析顺序MRO)
        for base_cls in cls.__mro__:
            # 跳过没有自定义__init__的父类(比如object)
            if '__init__' not in base_cls.__dict__:
                continue
            try:
                # 获取__init__方法的源代码
                source = inspect.getsource(base_cls.__init__)
                # 解析成抽象语法树(AST)
                tree = ast.parse(source)
                # 遍历所有赋值语句
                for node in ast.walk(tree):
                    if isinstance(node, ast.Assign):
                        # 检查赋值目标是不是self.xxx的形式
                        for target in node.targets:
                            if (isinstance(target, ast.Attribute) 
                                and isinstance(target.value, ast.Name) 
                                and target.value.id == 'self'):
                                attrs.add(target.attr)
            except TypeError:
                # 如果是内置类或者无法获取源代码的类,跳过
                continue
        # 转换成列表返回(顺序不固定,需要的话可以调整排序)
        return list(attrs)

class MyChildClass(MyClass):
    def __init__(self, z = None, **kwargs):
        self.c = z
        super().__init__(**kwargs)

>>> MyClass.get_all_attr()
['a', 'b']
>>> MyChildClass.get_all_attr()
['c', 'a', 'b']

优点:不需要创建实例,避免__init__的副作用;
缺点:依赖inspectast模块,而且如果__init__的代码是动态生成的、或者是C扩展类,可能无法获取源代码,导致收集不全。


备注:内容来源于stack exchange,提问作者Emke

火山引擎 最新活动