【问题标题】:Attrs dependant attributes on initializationAttrs 依赖于初始化的属性
【发布时间】:2021-10-09 13:45:24
【问题描述】:

假设我们有一个attrs 类:

@attr.s()
class Foo:
    a: bool = attr.ib(default=False)
    b: int = attr.ib(default=5)
    
    @b.validator
    def _check_b(self, attribute, value):
        if not self.a:
            raise ValueError("to define 'b', 'a' must be True")
        if value < 0:
            raise ValueError("'b' has to be a positive integer")

所以下面的行为是正确的:

>>> Foo(a=True, b=10)
Foo(a=True, b=10)

>>> Foo(b=10)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<attrs generated init __main__.Foo>", line 5, in __init__
    __attr_validator_b(self, __attr_b, self.b)
  File "<input>", line 9, in _check_b
ValueError: to define 'b', 'a' must be True

但这不是:

>>> Foo()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<attrs generated init __main__.Foo>", line 5, in __init__
    __attr_validator_b(self, __attr_b, self.b)
  File "<input>", line 9, in _check_b
ValueError: to define 'b', 'a' must be True

这显然是因为Foo.b 总是被初始化,不管Foo.a 何时被赋予值:通过默认值或Foo.__init__

是否有任何初始化钩子来完成这个属性依赖?

【问题讨论】:

    标签: python python-attrs


    【解决方案1】:

    @hynek's recommendation 具有导致有效实例的默认值之后,我已将依赖属性的default 更改为None 以仅在__init__ 上传递值时进行验证:

    @attr.s()
    class Foo:
        a: bool = attr.ib(default=False)
        b: Optional[int] = attr.ib(default=None)
        
        @b.validator
        def _check_b(self, attribute, value):
            if value is None:
                self.b = 5
                return
            if not self.a:
                raise ValueError("to define 'b', 'a' must be True")
            if value < 0:
                raise ValueError("'b' has to be a positive integer")
    

    我知道在验证器上更改属性值并不是最优的,但它可以完成工作。

    关于验证器的文档可以在here找到

    【讨论】:

      【解决方案2】:

      根据您的需要,您可以检查value 是否为默认值(在attribute.default 上可用)。但是,您的整个问题是,根据您的验证器,默认值会导致一个实例无效。因此,可能有更好的方法对此进行建模。

      【讨论】:

      • 问题是我无法区分__init__attribute.default 中给出的值。我已经尝试过使用默认值来生成有效实例的方法。
      • 我认为最直接的解决方案是使用某种哨兵值作为默认值,并使用__attrs_post_init__ 将它们替换为实际默认值并进行验证。正如您在这里所说的那样:您的问题与其说是依赖属性,不如说是检测参数是否已通过而不是默认值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-07-25
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      相关资源
      最近更新 更多