bux bux - 22 days ago 7
Python Question

Extend class attribute with Mixin

I want to write a Mixin who can extend eventual parent class atribute instead replace it. Example:

class Base(object):
my_attr = ['foo', 'bar']

class MyMixin(object):
my_attr = ['baz']

class MyClass(MyMixin, Base):
pass

print(MyClass.my_attr)



['baz']


How write
MyMixin
to have
MyClass.my_attr
with
['foo', 'bar', 'baz']
as value ?

Answer can be to do the merge in
MyClass
, but i want to know if it's possible to do it in MyMixin. Like with MetaClass or something else ?

Answer

If you really want to do this on class attributes with a mixin, you'll have to use a custom metaclass AND put the mixin after Base in the mro (which means it won't override anything in the base class, just add to it):

class Base(object):
    my_attr = ['foo', 'bar']

class MyMixinBase(type):
    def __init__(cls, name, parents, attribs):
        my_attr = getattr(cls, "my_attr", None)
        if my_attr is None:
            cls.my_attr = ["baz"]
        elif "baz" not in my_attr:
            my_attr.append("baz")


class MyMixin(object):
    __metaclass__ = MyMixinBase

class MyClass(Base, MyMixin):
    pass

print(MyClass.my_attr)

which makes for quite convoluted code... At this point, just using MyMixinBase as metaclass for MyClass would yield the same result:

class MyOtherClass(Base):
    __metaclass__ = MyMixinBase

print(MyOtherClass.my_attr)

and for such a use case, a class decorator works just fine:

def with_baz(cls):
    my_attr = getattr(cls, "my_attr", None)
    if my_attr is None:
        cls.my_attr = ["baz"]
    elif "baz" not in my_attr:
        my_attr.append("baz")
    return cls

@with_baz
class AnotherClass(Base):
    pass

print(AnotherClass.my_attr)