Pydantic模型中下划线属性名的处理机制及与普通Python类的差异
Pydantic如何处理带下划线的属性名?
在Pydantic模型中,带下划线的属性命名存在特殊行为,和普通Python类的表现不同,测试结果显示了一些看似“意外”的错误。
测试代码
from pydantic import BaseModel class PydanticClass(BaseModel): a: int = "AAA" _z: int = "_Z_Z_Z" @classmethod def get_a(cls): return cls.a @classmethod def get_z(cls): return cls._z class NonPydanticClass: a: int = "AAA" _z: int = "_Z_Z_Z" @classmethod def get_a(cls): return cls.a @classmethod def get_z(cls): return cls._z print("PYDANTIC MODEL CLASS TEST:") pydantic_instance = PydanticClass() try: msg1, msg2 = "SUCCESS", f"{pydantic_instance.get_a()}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "1 pydantic_instance.get_a()", "在类方法中访问非下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{pydantic_instance.get_z()}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "2 pydantic_instance.get_z()", "在类方法中访问下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{PydanticClass.a}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "3 PydanticClass.a ", "以‘静态属性’方式访问非下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{PydanticClass._z}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "4 PydanticClass._z", "以‘静态属性’方式访问下划线属性名", msg2)) print("\nNON PYDANTIC CLASS TEST:") nom_pydantic_instance = NonPydanticClass() try: msg1, msg2 = "SUCCESS", f"{nom_pydantic_instance.get_a()}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "1 class_instance.get_a()", "在类方法中访问非下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{nom_pydantic_instance.get_z()}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "2 class_instance.get_z()", "在类方法中访问下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{NonPydanticClass.a}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "3 PydanticClass.a ", "以‘静态属性’方式访问非下划线属性名", msg2)) try: msg1, msg2 = "SUCCESS", f"{NonPydanticClass._z}" except Exception as e: msg1, msg2 = "ERROR", f"{e}" print('{0:<7} :: {1:<27} :: {2:<65} :: {3}'.format(msg1, "4 PydanticClass._z", "以‘静态属性’方式访问下划线属性名", msg2))
测试结果
PYDANTIC MODEL CLASS TEST: ERROR :: 1 pydantic_instance.get_a() :: 在类方法中访问非下划线属性名 :: type object 'PydanticClass' has no attribute 'a' SUCCESS :: 2 pydantic_instance.get_z() :: 在类方法中访问下划线属性名 :: _Z_Z_Z ERROR :: 3 PydanticClass.a :: 以‘静态属性’方式访问非下划线属性名 :: type object 'PydanticClass' has no attribute 'a' SUCCESS :: 4 PydanticClass._z :: 以‘静态属性’方式访问下划线属性名 :: _Z_Z_Z NON PYDANTIC CLASS TEST: SUCCESS :: 1 class_instance.get_a() :: 在类方法中访问非下划线属性名 :: AAA SUCCESS :: 2 class_instance.get_z() :: 在类方法中访问下划线属性名 :: _Z_Z_Z SUCCESS :: 3 PydanticClass.a :: 以‘静态属性’方式访问非下划线属性名 :: AAA SUCCESS :: 4 PydanticClass._z :: 以‘静态属性’方式访问下划线属性名 :: _Z_Z_Z
原因解释
Pydantic的核心逻辑是:
- 非下划线开头的属性会被识别为模型字段,这些字段不会保留为类的直接属性,而是被存储到模型的
__fields__字典中。所以直接通过PydanticClass.a访问会报错,类方法里的cls.a也找不到该属性——因为它已经不是类的固有属性了。 - 下划线开头的属性不会被Pydantic当作模型字段处理,完全保留了普通Python类属性的特性,因此不管是通过类直接访问,还是在类方法里调用,都能正常获取到值。
普通Python类没有这种字段处理逻辑,所有属性都会直接绑定在类上,因此不管属性名是否带下划线,所有访问方式都能成功。
内容的提问来源于stack exchange,提问作者villamejia
相关产品推荐
相关产品推荐

