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

如何实现类属性列表方法,区分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赋值None
  • test3正常初始化所有属性
  • test4忽略字典中的额外键random,只初始化类定义的属性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 06:05:56