如何通过通用Pythonic方式获取类实例属性列表?优先支持预实例化检查
获取类实例属性列表的通用解决方案
需求
- 优先实现无需实例化类就能获取实例属性列表(预实例化检查)
- 若无法实现,寻求适用于通用类的最Pythonic实现方式
当前尝试及问题
可正常运行的基础实现
from typing import List import inspect import datetime as dt class Apple: def __init__(self, color: str, size: int) -> None: self.log_time = dt.datetime.now() self.color = color self.size = size def get_attributes(class_obj: object) -> List[str]: '''Returns instantiated class attributes.''' params = list(inspect.signature(class_obj).parameters.keys()) kwargs = {param: None for param in params} instance = class_obj(**kwargs) attributes = list(vars(instance)) return attributes # 运行结果 # >>> ['log_time', 'color', 'size']
这个方法在__init__能接受None作为参数时可以正常工作,但遇到__init__中有参数运算的类就会报错:
触发错误的示例类
class Orange: def __init__(self, color: str, size: int) -> None: self.log_time = dt.datetime.now() self.color = color self.size = size self.another_attribute = size * 2 # 调用get_attributes(Orange)会触发如下错误: # --------------------------------------------------------------------------- # TypeError Traceback (most recent call last) # Cell In[63], line 1 # ----> 1 get_attributes(Orange) # ... # TypeError: unsupported operand type(s) for *: 'NoneType' and 'int'
原因是__init__中存在size * 2的运算,传入None会触发类型错误,而用特定哑参数的方案不具备通用性。
解决方案
一、无需实例化的方案(局限性说明)
首先明确:完全不实例化就获取所有实例属性是不可能的,因为很多属性是在__init__甚至后续方法中动态生成的(比如示例中的log_time、another_attribute),这些属性只有在实例化过程中才会被绑定到实例上,类本身的定义里不会提前包含这些动态属性。
如果你的类是使用dataclasses或者attrs库定义的,可以直接从类的字段信息中获取属性列表,无需实例化:
from dataclasses import dataclass, fields @dataclass class AppleDataclass: color: str size: int log_time: dt.datetime = None # 获取属性列表 print([field.name for field in fields(AppleDataclass)]) # >>> ['color', 'size', 'log_time']
但这种方案只适用于用特定库定义的类,对普通自定义类不通用。
二、通用实例化方案(Pythonic实现)
对于普通自定义类,必须实例化才能获取所有动态生成的属性,我们可以通过构造通用哑参数的策略来避免__init__中的运算错误:
from typing import List import inspect import datetime as dt from unittest.mock import MagicMock def get_attributes_generic(class_obj: type) -> List[str]: sig = inspect.signature(class_obj.__init__) kwargs = {} for param_name, param in sig.parameters.items(): if param_name == 'self': continue # 优先根据类型注解生成合法哑值,否则用MagicMock模拟任意操作 if param.annotation is not param.empty: try: # 尝试实例化类型(比如str()得到空串、int()得到0) kwargs[param_name] = param.annotation() except (TypeError, ValueError): # 无法实例化的类型(如泛型、抽象类)用MagicMock kwargs[param_name] = MagicMock() else: kwargs[param_name] = MagicMock() # 创建实例并获取所有属性 instance = class_obj(**kwargs) return list(vars(instance)) # 测试Apple类 print(get_attributes_generic(Apple)) # >>> ['log_time', 'color', 'size'] # 测试Orange类 print(get_attributes_generic(Orange)) # >>> ['log_time', 'color', 'size', 'another_attribute']
方案说明
- 参数处理逻辑:
- 自动跳过
self参数 - 利用类型注解生成合法哑值,避免
None导致的运算错误 - 对无法实例化的类型,用
MagicMock模拟任意操作,确保__init__能正常执行
- 自动跳过
- 通用性:适配绝大多数自定义类,不管
__init__中有没有参数运算,都能成功创建实例并获取所有属性 - 局限性:如果
__init__依赖真实的外部资源(如数据库连接),MagicMock可能无法完全模拟,但这种场景需要结合业务需求调整实现逻辑
内容的提问来源于stack exchange,提问作者agftrading
相关产品推荐
相关产品推荐

