Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheriting the class dictionary with metaclasses

I am setting a class property fields using a metaclass:

class MyMeta(type):
    def __new__(mcs, name, bases, clsdict):

        clsdict['fields'] = {k: v
                             for k, v in clsdict.items()
                             if <my_condition>}
        return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)

class MyBaseClass(metaclass=MyMeta):
    fields = {}

The following instantiation leads to expected results:

class SubClass(MyBaseClass):
    param1 = 1 # meets <my_condition>

>>> SubClass.fields
{param1: 1}

But if I now subclass SubClass, fields is empty:

class SubSubClass(SubClass):
   pass

>>> SubSubClass.fields 
{}

How would I be able to update the classdict of all classes in inheritance hierarchy so that the fields variable would be updated from base classes?

like image 584
ProfHase85 Avatar asked Apr 28 '17 16:04

ProfHase85


1 Answers

You need to somehow keep the fields of the superclasses, for example by iterating over the "bases" and using their fields as starting point:

class MyMeta(type):
    def __new__(mcs, name, bases, clsdict):
        if 'fields' not in clsdict:
            clsdict['fields'] = {}
        # Initialize "fields" from base classes
        for base in bases:
            try:
                clsdict['fields'].update(base.fields)
            except AttributeError:
                pass
        # Fill in new fields (I included a "trivial" condition here, just use yours instead.)
        clsdict['fields'].update({k: v for k, v in clsdict.items() if k.startswith('param')})
        return super(MyMeta, mcs).__new__(mcs, name, bases, clsdict)

And it works for SubClass and SubSubClass:

>>> SubClass.fields
{'param1': 1}

>>> SubSubClass.fields
{'param1': 1}
like image 114
MSeifert Avatar answered Sep 22 '22 11:09

MSeifert