【问题标题】:Python dataclass, what's a pythonic way to validate initialization arguments?Python数据类,验证初始化参数的pythonic方法是什么?
【发布时间】:2020-05-27 12:09:43
【问题描述】:

在实例化之前验证 init 参数的 Pythonic 方法是什么?

我认为也许利用__new__ dunder 方法是合适的?

from dataclasses import dataclass

@dataclass
class MyClass:
    is_good: bool = False
    is_bad: bool = False

    def __new__(cls, *args, **kwargs):
        instance: cls = super(MyClass, cls).__new__(cls, *args, **kwargs)
        if instance.is_good:
            assert not instance.is_bad
        return instance

【问题讨论】:

  • 您是否考虑过简单地禁止调用者为这两个字段指定值?例如设置is_bad: bool = field(init=False),然后在__post_init__中设置self.is_bad = not self.is_good
  • thanks @chepner field(init=False) 也是一个很酷的主意。

标签: python python-3.x python-dataclasses


【解决方案1】:

定义a __post_init__ method on the class;如果定义,生成的__init__ 将调用它:

from dataclasses import dataclass

@dataclass
class MyClass:
    is_good: bool = False
    is_bad: bool = False

    def __post_init__(self):
        if self.is_good:
            assert not self.is_bad

the replace function 用于创建新实例时,这甚至可以工作。

【讨论】:

  • 数据类文档写得很好,哎呀,要是我再多一点耐心就好了。谢谢!
【解决方案2】:

dataclasses 模块 made a conscious decision 的作者不要实现存在于类似第三方项目(如 attrspydanticmarshmallow)中的验证器。如果您的实际问题在您发布的问题范围内,那么在__post_init__ 中进行验证是完全可以的。

但是,如果您有更复杂的验证过程或使用继承之类的东西,您可能想要使用我提到的更强大的库之一,而不是 dataclass。只是为了看看,这就是您的示例使用pydantic 的样子:

>>> from pydantic import BaseModel, validator
>>> class MyClass(BaseModel):
...     is_good: bool = False
...     is_bad: bool = False
...
...     @validator('is_bad')
...     def check_something(cls, v, values):
...         if values['is_good'] and v:
...             raise ValueError("Can't be both good an bad now, can it?")
...         return v
...     
>>> MyClass(is_good=True, is_bad=False)  # this would be a valid instance
MyClass(is_good=True, is_bad=False)
>>> MyClass(is_good=True, is_bad=True)   # this wouldn't
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "pydantic/main.py", line 283, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for MyClass
is_bad
  Can't be both good an bad now, can it? (type=value_error)

【讨论】:

    【解决方案3】:

    你可以试试这个:

    from dataclasses import dataclass
    
    from validated_dc import ValidatedDC
    
    
    @dataclass
    class MyClass(ValidatedDC):
        is_good: bool = False
        is_bad: bool = False
    
    
    instance = MyClass()
    assert instance.get_errors() is None
    assert instance == MyClass(is_good=False, is_bad=False)
    
    data = {'is_good': True, 'is_bad': True}
    instance = MyClass(**data)
    assert instance.get_errors() is None
    
    data = {'is_good': 'bad_value', 'is_bad': True}
    instance = MyClass(**data)
    assert instance.get_errors()
    print(instance.get_errors())
    # {'is_good': [BasicValidationError(value_repr='bad_value', value_type=<class 'str'>, annotation=<class 'bool'>, exception=None)]}
    
    # fix
    instance.is_good = True
    assert instance.is_valid()
    assert instance.get_errors() is None
    

    验证DC:https://github.com/EvgeniyBurdin/validated_dc

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 2012-01-14
      • 2011-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-12
      相关资源
      最近更新 更多