3

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?

karlosss
  • 2,816
  • 7
  • 26
  • 42
  • You need to implement `__getattr__` and `__setattr__` on the *metaclass*, but then you have to use the metaclass to generate the class, I believe – juanpa.arrivillaga Apr 11 '19 at 22:22
  • You could instead create the generated class in one line via: `type("GeneratedClass", (Inherited,), {'__getattr__': your_getattr_impl})`, and `your_getattr_impl` should have a signature `(self, name)` (`self` rather than `cls` because the instance of the class will be passed when used), unless you want to do this at the metaclass level. Also you really don't need metaclass to dynamically create class with custom attribute for dunder functions. What exactly are you trying to achieve? – metatoaster Apr 11 '19 at 22:24
  • @metatoaster they are trying to implement those sunder methods on the metaclass so it will work with class attributes – juanpa.arrivillaga Apr 11 '19 at 22:26
  • @juanpa.arrivillaga I can see that, but unless there are good reasons to do so overriding both metaclass and making use of dynamic class construction is almost always overkill. My point is to suggest to OP to take one technique unless there are good reasons to use both of them. – metatoaster Apr 11 '19 at 22:29
  • You should just be able to use `MyCustomMeta(...,...,...)` in the same way you use `type(...,...,...)`, but they just have to actually implement `MyCustomMeta` I mean, all of this is probably overkill. Why stop now... – juanpa.arrivillaga Apr 11 '19 at 22:31

1 Answers1

2

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,), {})
jsbueno
  • 99,910
  • 10
  • 151
  • 209