Search code examples
pythonmetaprogrammingmetaclass

Implicit call of a base metaclass init function in Python 3


Having a code below:

class BaseMeta(type):
    def __init__(self, cls_name, cls_bases, cls_dict):
        super(BaseMeta, self).__init__(cls_name, cls_bases, cls_dict)
        print("BaseMeta init")
        print(cls_dict)

class Proxy():
    class __metaclass__(BaseMeta, type):
        def __new__(cls, name, bases, dict):
            return type.__new__(cls, name, bases, dict)

        def __init__(cls, name, bases, dict):
            print("Proxy init")
            BaseMeta.__init__(cls, name, bases, dict)

class Service(Proxy):
    def some_endpoint(self, a):
        print(a)

When running in Python 2, output would be similar to:

Proxy init
BaseMeta init
{'__module__': '__main__', '__metaclass__': <class '__main__.__metaclass__'>}
Proxy init
BaseMeta init
{'some_endpoint': <function some_endpoint at 0x8011e35d0>, '__module__': '__main__'}

BaseMeta init is called implicitly on a script init (twice_

In python 3, nothing will be printed at all. Assuming you can't change BaseMeta and Service classes, how would you modify Proxy class so you can see the output similar to output above in Python 3? Thanks.


Solution

  • The probem is that __metclass__ no longer has any special status in Python 3. Normally, you specify the metaclass like an argument in the class definition statement:

    class Foo(metaclass=FooMeta):
        ...
    

    You can do something like this:

    class BaseMeta(type):
        def __init__(self, cls_name, cls_bases, cls_dict):
            super(BaseMeta, self).__init__(cls_name, cls_bases, cls_dict)
            print("BaseMeta init")
            print(cls_dict)
    
    class ProxyMeta(BaseMeta):
        def __new__(cls, name, bases, dict):
            return type.__new__(cls, name, bases, dict)
        def __init__(cls, name, bases, dict):
            print("Proxy init")
            BaseMeta.__init__(cls, name, bases, dict)
    
    class Proxy(metaclass=ProxyMeta):
        pass
    
    class Service(Proxy):
        def some_endpoint(self, a):
            print(a)