Marco Buttu wrote: > Hi all, I have a question about class creation and the __call__ method. > I have the following metaclass: > > >>> class FooMeta(type): > ... def __call__(metacls, name, bases, namespace): > ... print("FooMeta.__call__()") > > > From what I undestood, at the end of the class statement happens > something like this: > > >>> def __call__(metacls, name, bases, namespace): > ... print("FooMeta.__call__()") > ... > >>> FooMeta = type('FooMeta', (type,), {'__call__': __call__}) > > The call to the metaclass type causes the call to type.__call__(), so > that's happened is: > > >>> FooMeta = type.__call__(type, 'FooMeta', (type,), {'__call__': > __call__}) > > Now I expected the output `FooMeta.__call__()` from the following Foo > class creation: > > >>> class Foo(metaclass=FooMeta): > ... pass > > because I thought at the end of the class Foo suite this should have > been happened: > > >>> Foo = FooMeta.__call__(FooMeta, 'Foo', (), {}) > FooMeta.__call__() > > but I thought wrong: > > >>> class FooMeta(type): > ... def __call__(metacls, name, bases, namespace): > ... print("FooMeta.__call__()") > ... > >>> class Foo(metaclass=FooMeta): > ... pass > ... > >>> > > How come? Is it because the first argument of metaclass.__call__() is > always type or I am thinking something wrong? > Thanks in advance, Marco
Forget about metaclasses for the moment and ask yourself what happens when a regular class class A: def __init__(...): ... def __call__(...): ... is "called": a = A(...) # invokes __init__() a(...) # invokes __call__() The metaclass is just the class of a class, i. e. the Foo object is an instance of FooMeta, so making Foo invokes (__new__() and) __init__(), and calling Foo invokes FooMeta.__call__(): >>> class FooMeta(type): ... def __call__(self, *args): print("__call__%r" % (args,)) ... >>> class Foo(metaclass=FooMeta): pass ... >>> Foo() __call__() If you follow that logic you can easily see that for FooMeta to invoke your custom __call__() method you'd have to define it in FooMeta's metaclass: >>> class FooMetaMeta(type): ... def __call__(*args): print(args) ... >>> class FooMeta(metaclass=FooMetaMeta): ... pass ... >>> class Foo(metaclass=FooMeta): ... pass ... (<class '__main__.FooMeta'>, 'Foo', (), {'__module__': '__main__', '__qualname__': 'Foo'}) >>> Foo is None >>> >>> True So, yes, it's turtles all the way down... -- https://mail.python.org/mailman/listinfo/python-list