Python 3.7 is around the corner,我想测试一些新的dataclass
+输入特性。对于本机类型和来自typing
模块的类型,获得正确的提示非常容易:
>>> import dataclasses
>>> import typing as ty
>>>
... @dataclasses.dataclass
... class Structure:
... a_str: str
... a_str_list: ty.List[str]
...
>>> my_struct = Structure(a_str='test', a_str_list=['t', 'e', 's', 't'])
>>> my_struct.a_str_list[0]. # IDE suggests all the string methods :)
但是我想尝试的另一件事是在运行时强制类型提示作为条件,也就是说,具有不正确类型的dataclass
不应该存在。它可以通过^{
>>> @dataclasses.dataclass
... class Structure:
... a_str: str
... a_str_list: ty.List[str]
...
... def validate(self):
... ret = True
... for field_name, field_def in self.__dataclass_fields__.items():
... actual_type = type(getattr(self, field_name))
... if actual_type != field_def.type:
... print(f"\t{field_name}: '{actual_type}' instead of '{field_def.type}'")
... ret = False
... return ret
...
... def __post_init__(self):
... if not self.validate():
... raise ValueError('Wrong types')
这种validate
函数适用于本机类型和自定义类,但不适用于typing
模块指定的类型和自定义类:
>>> my_struct = Structure(a_str='test', a_str_list=['t', 'e', 's', 't'])
Traceback (most recent call last):
a_str_list: '<class 'list'>' instead of 'typing.List[str]'
ValueError: Wrong types
有没有更好的方法用typing
类型的列表验证非类型列表?最好是不包括检查任何list
、dict
、tuple
或set
(即dataclass
属性)中的所有元素的类型。
您应该使用
isinstance
,而不是检查类型是否相等。但不能使用参数化泛型类型(typing.List[int]
)来执行此操作,必须使用“泛型”版本(typing.List
)。因此您可以检查容器类型,但不能检查包含的类型。参数化泛型类型定义了一个__origin__
属性,您可以使用它。与Python3.6相反,在Python3.7中,大多数类型提示都有一个有用的
__origin__
属性。比较:以及
Python 3.8引入了对^{} 内省函数的更好支持:
值得注意的例外是
typing.Any
、typing.Union
和typing.ClassVar
……好吧,任何typing._SpecialForm
都不定义__origin__
。幸运的是:但是参数化类型定义了一个} 函数来检索它们:
__args__
属性,该属性将参数存储为元组;Python 3.8引入了^{所以我们可以改进一下类型检查:
这不是完美的,因为它不会解释例如
typing.ClassVar[typing.Union[int, str]]
或typing.Optional[typing.List[int]]
,但是它应该开始工作。接下来是申请这张支票的方法。
我不使用
__post_init__
,而是使用decorator路径:这可以用于任何具有类型提示的对象,而不仅仅是dataclasses
:用法是:
显然,通过验证前一节中建议的一些类型提示,这种方法仍然有一些缺点:
class Foo: def __init__(self: 'Foo'): pass
)的类型提示不被inspect.getfullargspec
考虑:您可能需要使用^{未验证不是适当类型的默认值:
不会引发任何} 与^{} 结合使用;
TypeError
。如果您想解释这一点(从而迫使您定义def foo(bar: typing.Optional[int] = None)
),您可能需要将^{def foo(*args: typing.Sequence, **kwargs: typing.Mapping)
的内容,并且,正如前面所说的,我们只能验证容器而不能验证包含的对象。感谢@Aran-Fey帮助我改进了这个答案。
刚找到这个问题。
pydantic可以对数据类进行开箱即用的完整类型验证。(承认:我建造了pydantic)
只要使用pydantic版本的decorator,得到的数据类就完全是普通的。
最后一行将给出:
相关问题 更多 >
编程相关推荐