Override a keyvalue that is passed to a metaclass of a Baseclass by its childClass

24 Views Asked by At

When I derive the BaseClass by the ChildClass, the BaseMetaClass.__new__(...) is run twice. First with the keywordarg "foo" and second time with "bar". Is it possible to override "foo" with "bar" before it is executed in the first place. I don't care if it is run twice but in the first execution it should have the value of "bar".

class BaseMetaClass(type):
    def __new__(cls, name, bases, attrs, *args, **kwargs):
        # do something with attrs here
        return super().__new__(cls, name, bases, attrs)

class BaseClass(keywordarg="foo", metaclass=BaseMetaClass):
    pass

class ChildClass(BaseClass, keywordarg="bar"):
    pass

Why am I trying this:

I am trying this because I am figuring out how to override/ manipulate a decorator that registers a service.

Things I have tried in addition

I have already tried __prepare__ which did not lead to a the outcome I was hoping for.

I also tried following which seems not to be a valid syntax

class ChildClass(BaseClass(keywordarg="bar)):
1

There are 1 best solutions below

1
jsbueno On

In the code you provide, BaseMetaclass.__new__ is executed exactly once for BaseClass and then, exact once for ChildClass.

I can't imagine why you think it could be different. THe creation of ChildClass itself will only run the metaclass __new__ code once, with the custom named arguments passed in that child class.

That is easy to visualize by simply adding a print call to to __new__:

class BaseMetaClass(type):
    def __new__(cls, name, bases, attrs, *args, **kwargs):
        # do something with attrs here
        print(name, kwargs)
        return super().__new__(cls, name, bases, attrs)

class BaseClass(keywordarg="foo", metaclass=BaseMetaClass):
    pass

class ChildClass(BaseClass, keywordarg="bar"):
    pass

And running this snippet outputs:

BaseClass {'keywordarg': 'foo'}
ChildClass {'keywordarg': 'bar'}

If you don't want "keywordarg" to be set when creating "BaseClass" at all, just don't pass it as a named argument. Otherwise, the code is working as expcted, and BaseMetaClass.__new__ is executed a single time, with the argument bar, when the class is created.

Python can't "know beforehand" that ChildClass will inherit from BaseClass and change "keywordarg" when creating BaseClass - and even if it could be possible (it could by reading the source file itself and resolving references, essentially interpreting the Python code before it would be executed), what should it do if more than one class would derive from "BaseClass"? WHich of the children parameters it should pick?

You mention you are trying this in order to "register a service" - it looks like you have a "XY" problem: what you are trying to do is not how class creation, customized or not, work. Just spell what you are actually trying to achieve there, not this one step you thought you might need.