如何实现类属性列表方法,区分Optional与非可选属性?
需求:实现区分类可选/非可选属性的方法
我需要实现一个方法attributes_lister,能够列出某个类的所有属性,并且区分其中的可选(Optional)属性与非可选属性。该方法需要适配如下示例场景:
from typing import Dict, Optional, Union class TestClass: id: int name: Optional[str] @staticmethod def from_dict(testclass_dict: Dict[str, Union[int, str]]): test: TestClass = TestClass() non_opt_attributes, opt_attributes = attributes_lister(TestClass) for attribute_name in non_opt_attributes: try: setattr(test, attribute_name, testclass_dict[attribute_name]) except KeyError as ke: print(f"{attribute_name} is a non optional attribute of TestClass") return None for attribute in opt_attributes: try: setattr(test, attribute_name, testclass_dict[attribute_name]) except KeyError as ke: setattr(test, attribute_name, None) return test
预期效果
上述代码预期实现的调用场景:
test1: TestClass = TestClass.from_dict({"name": "name"}) test2: TestClass = TestClass.from_dict({"id": 5}) test3: TestClass = TestClass.from_dict({"id": 5, "name": "name"}) test4: TestClass = TestClass.from_dict({"id": 5, "name": "name", "random": "value"})
对应的输出结果:
test1 = None test2 = TestClass(id=5) test3 = TestClass(id=5, name="name") test4 = TestClass(id=5, name="name")
编辑说明:根据@user2357112的评论,我修正了代码逻辑——明确Optional并非允许属性缺失,而是允许其值为None,但这并不影响原问题的核心需求。
实现方案
核心方法attributes_lister实现
利用typing.get_type_hints解析类的类型注解,通过判断类型是否为Union[T, None](即Optional[T]的本质)来区分可选与非可选属性:
from typing import get_type_hints, Union, UnionType def attributes_lister(cls): # 获取类的所有类型注解 type_hints = get_type_hints(cls) non_opt_attrs = [] opt_attrs = [] for attr_name, attr_type in type_hints.items(): is_optional = False # 兼容Python 3.10+的UnionType和旧版本的Union if isinstance(attr_type, (Union, UnionType)): if type(None) in attr_type.__args__: is_optional = True # 处理泛型类型的情况(如通过from __future__ import annotations的延迟注解) elif hasattr(attr_type, '__origin__') and attr_type.__origin__ is Union: if type(None) in attr_type.__args__: is_optional = True if is_optional: opt_attrs.append(attr_name) else: non_opt_attrs.append(attr_name) return non_opt_attrs, opt_attrs
修正原from_dict方法的bug
原代码中第二个循环存在变量名错误(误写为attribute_name而非当前循环的attribute),修正后的完整代码:
from typing import Dict, Optional, Union, get_type_hints, UnionType def attributes_lister(cls): type_hints = get_type_hints(cls) non_opt_attrs = [] opt_attrs = [] for attr_name, attr_type in type_hints.items(): is_optional = False if isinstance(attr_type, (Union, UnionType)): if type(None) in attr_type.__args__: is_optional = True elif hasattr(attr_type, '__origin__') and attr_type.__origin__ is Union: if type(None) in attr_type.__args__: is_optional = True if is_optional: opt_attrs.append(attr_name) else: non_opt_attrs.append(attr_name) return non_opt_attrs, opt_attrs class TestClass: id: int name: Optional[str] @staticmethod def from_dict(testclass_dict: Dict[str, Union[int, str]]): test: TestClass = TestClass() non_opt_attributes, opt_attributes = attributes_lister(TestClass) for attribute_name in non_opt_attributes: try: setattr(test, attribute_name, testclass_dict[attribute_name]) except KeyError as ke: print(f"{attribute_name} is a non optional attribute of TestClass") return None # 修正变量名错误 for attribute_name in opt_attributes: try: setattr(test, attribute_name, testclass_dict[attribute_name]) except KeyError as ke: setattr(test, attribute_name, None) return test
验证效果
运行测试代码后,结果完全符合预期:
test1因缺少必填属性id返回None,并打印提示信息test2自动为可选属性name赋值Nonetest3正常初始化所有属性test4忽略字典中的额外键random,只初始化类定义的属性
内容的提问来源于stack exchange,提问作者GregoirePelegrin
相关产品推荐
相关产品推荐

