4

Using the Python module attrs, I'm trying to have a subclass with a condition on its attributes more restrictive than its parent, like in the minimal example below.

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()

Defining a validator like above raises an error (NameError: name 'x' is not defined).

I've found a workaround by redefining x in the child class.

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

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

However it messes up the ordering of the attributes.

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

So, finally, I ended up redefining all the attributes in the child class (and there are more than two in the actual code).

Is there a more elegant way to do this?

Matthieu
  • 81
  • 4
  • I have no experience with `python-attrs` (which IMHO is a typical "not such a good idea" ie something that tends to creates more problems than it solves and makes code more complicated than it has to be), but anyway: have you tried `@Base.x.validator` ? Note that I actually don't think it will work as intended (I suspect it will apply the validator to `Base.x`, hence to `Base` and all of it's subclasses). – bruno desthuilliers Feb 14 '18 at 13:34
  • Also note than adding restrictions to a child class breaks the Liskov's substitution principle (https://en.wikipedia.org/wiki/Liskov_substitution_principle) so your child class is no longer a proper subtype of it's parent. It's not necessarily a problem in Python where inheritance is not required for subtyping, but I thought I'd better mention it since it might reveal a design issue. – bruno desthuilliers Feb 14 '18 at 13:37

1 Answers1

4

Finally, the simplest workaround I found was simply not to use a validator in the child class:

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

Thanks to bruno desthuilliers for rightly pointing out Liskov principle. Note that the problem does not only occur for @x.validator but also for other constructs such as the attribute default value @x.default. It seems that the upcoming dataclass for Python 3.7 works slightly better with inheritance than attrs in this case.

Matthieu
  • 81
  • 4