Search code examples
pythonclassmetaclassgetattr

__getattr__ and __getattribute__ for class/static atributes of dynamically generated classes


This question answers how to implement __getattr__ for static/class attributes - using a metaclass. However, I would like to implement __getattr__ and __getattribute__ for a class generated by type() and to make things even more interesting, the class inherits a class which has a custom metaclass which must be executed properly.

The code summarizing the paragraph above:

class Inherited(metaclass=SomeFancyMetaclass):
    ...

generated_class = type("GeneratedClass", (Inherited,), {})

def __class_getattr__(cls, name):  # __getattr__ for class, not sure how the code shall look exactly like
    return getattr(cls, name)

setattr(generated_class, "__getattr__", __class_getattr__)  # similarly for __getattribute__

The question: is this possible, and if so, how? Could someone provide a minimal working example?


Solution

  • Just make your metaclass inherit from SomeFancyMetaclass, implement the __getattr__ (and __getattribute__) there properly, and use this metaclass, rather than a call to type to generate your inheited, dynamic class.

    Although you are using a lot of seldom used stuff, there are no special mechanisms in the way - it should be plain Python -

    Of course, you did not tell what you want to do in the metaclass special methods - there might be some black magic to be performed there - and if you are doing __getattribute__, you always have to be extra careful, and redirect all attrbiutes that you don't care about to the super-call, otherwise, nothing works.

    Also, keep in mind that the attribute-access ustomization possible with both methods won't work to "create magic dunder methods" - that is: your class won't magically have an __add__ or __dir__ method because your metaclass __getattribute__ generates one - rather, these are fixed in spcial slots by the Python runtime, and their checking and calling bypasses normal attribute lookup in Python.

    Otherwise:

    class Inherited(metaclass=SomeFancyMetaclass):
        ...
    
    class MagicAttrsMeta(Inherited.__class__):
        def __getattr__(self, attr):
              if attr in ("flying", "circus", "brian", "king_arthur"):
                   return "coconut"
              raise AttributeError()
    
    
    generated_class = MagicAttrsMeta("GeneratedClass", (Inherited,), {})