Consider this minimal example:
from attrs import define, field, validators
@define(kw_only=True)
class Vehicle:
num_wheels: int = field(validator=validators.instance_of(int))
@num_wheels.validator
def validate_num_wheels(self, attribute, value) -> None:
if value <= 0:
raise ValueError("Number of wheels must be greater than 0")
How can I create subclasses of this parent class which set their own default values of num_wheels
but still run the validators defined in the parent class? For example:
@define
class Car(Vehicle):
# num_wheels: int = 4 #! this doesn't work
pass
@define
class Motorbike(Vehicle):
# num_wheels: int = 2
pass
I want to protect against Car(num_wheels=3.14)
or Car(num_wheels=-1)
. But also want Car()
or Motorbike()
to initialise with default values.
I've seen this answer which might be relevant - https://stackoverflow.com/a/67129088/11432894. It suggests this might be a solution?
@define
class Car(Vehicle):
def __init__(self, *, num_wheels: int = 4, **kwargs):
self.__attrs_init__(num_wheels=num_wheels, **kwargs)
It seems like at the core you would like to re-use field definitions which is unfortunately an open issue: https://github.com/python-attrs/attrs/issues/637
As mentioned in one of the comments, in this case the number of wheels should probably be part of the class – not an attribute. I guess you could also make your Vehicle class "abstract" and write a validator that reads the attribute from the class (i.e. it would check self.num_wheels
with num_wheels
being a ClassVar
).