attrs
attrs copied to clipboard
Make parent class initalization easy
In some cases it is useful to create an new object by copying some of the attributes that are provided by a shared base class. Here is an example
import attr
@attr.s()
class Base(object):
uid = attr.ib()
name = attr.ib()
@attr.s()
class AreaChild(Base):
area = attr.ib()
@attr.s()
class LengthChild(Base):
length = attr.ib()
def calc_square(length_obj):
def make_class_filter(cls):
cls_fields = attr.fields_dict(cls)
return lambda a,v: a.name in cls_fields
base_dict = attr.asdict(length_obj,
filter = make_class_filter(Base))
return AreaChild(area = length_obj.length**2,
**base_dict)
lc = LengthChild(uid=42, name='rope', length=3)
print(lc)
ac = calc_square(lc)
print(ac)
As one can see extraction of the base class attributes is somewhat clunky. The calc_square function could be much easier to read if a base class could be passed as a filter to .asdict:
def calc_square(length_obj):
base_dict = attr.asdict(length_obj, filter = Base)
return AreaChild(area=length_obj.length**2, **base_dict)
If you would think this makes sense, I could provide a pull request to enable classes as filters with the functionality as described above.
Hmm so first I started writing a long-ish post about a more general problem that I keep running into (basically have a base class and pull stuff from a DB and instantiate subclasses) but your example is different and I really want to say slightly convoluted because you compute a class (AreaChild) based on another subclass of Base (LengthChild) and copy around the base data.
I'm on record preaching composition, but this one is screaming for it. What is stopping you from doing:
import attr
@attr.s()
class Base(object):
uid = attr.ib()
name = attr.ib()
@attr.s()
class AreaChild(object):
base = attr.ib()
area = attr.ib()
@attr.s()
class LengthChild(object):
base = attr.ib()
length = attr.ib()
def calc_square(length_obj):
return AreaChild(base=length_obj.base, area=length_obj.length**2)
lc = LengthChild(Base(uid=42, name='rope'), length=3)
print(lc)
ac = calc_square(lc)
print(ac)
It's more concise and clearer in intent, no?