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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With