【问题标题】:python-attrs: validator in child classpython-attrs:子类中的验证器
【发布时间】:2018-07-25 02:11:01
【问题描述】:

使用 Python 模块 attrs,我试图创建一个子类,其属性条件比其父类更严格,如下面的最小示例所示。

import attr

@attr.s
class Base:
    x = attr.ib()
    y = attr.ib()

@attr.s
class Child(Base):

    @x.validator
    def _x_validator(self, a, x):
        if x < 0:
            raise ValueError()

像上面那样定义验证器会引发错误 (NameError: name 'x' is not defined)。

我通过在子类中重新定义 x 找到了解决方法。

@attr.s
class Child(Base):
    x = attr.ib()

    @x.validator
    def _x_validator(self, a, x):
        if x < 0:
            raise ValueError()

但是它打乱了属性的顺序。

In [5]: Child(5, 10)
Out[5]: Child(y=5, x=10)

所以,最后,我最终重新定义了子类中的所有属性(实际代码中不止两个)。

有没有更优雅的方法来做到这一点?

【问题讨论】:

  • 我没有使用python-attrs 的经验(恕我直言,这是一个典型的“不是一个好主意”,即往往会产生比它解决的问题更多的问题,并使代码比它必须的更复杂),但无论如何:你试过@Base.x.validator 吗?请注意,我实际上不认为它会按预期工作(我怀疑它会将验证器应用于Base.x,因此应用于Base 及其所有子类)。
  • 另外请注意,向子类添加限制会破坏 Liskov 的替换原则 (en.wikipedia.org/wiki/Liskov_substitution_principle),因此您的子类不再是其父类的正确子类型。在子类型化不需要继承的 Python 中,这不一定是个问题,但我想我最好提一下,因为它可能会揭示设计问题。

标签: python python-attrs


【解决方案1】:

最后,我发现的最简单的解决方法就是不在子类中使用验证器:

@attr.s
class Child(Base):
    def __attrs_post_init__(self):
        assert self.x > 0

感谢 bruno desthuilliers 正确地指出了 Liskov 原则。 请注意,该问题不仅发生在@x.validator 上,还发生在其他构造上,例如属性默认值@x.default。在这种情况下,用于 Python 3.7 的 works slightly better with inheritance 似乎即将推出的 dataclassattrs

【讨论】:

    猜你喜欢
    • 2021-03-25
    • 2019-01-29
    • 2010-10-04
    • 2018-04-22
    • 2018-05-27
    • 1970-01-01
    • 2021-02-23
    • 2021-05-28
    • 1970-01-01
    相关资源
    最近更新 更多