Why metaclass's __call__ method called on class, but native class's __call__ not?

538 views Asked by At
class Meta(type):
    def __call__(cls, *args, **kwargs):
        print 'Meta.__call__ called.'
        return super(Meta, cls).__call__(*args, **kwargs)


class MetaInstance(object):
    __metaclass__ = Meta

# Instantiate class.
MetaInstance()  # Meta.__call__ called.


class StandardClass(object):
    @classmethod
    def __call__(cls, *args, **kwargs):
        print 'StandardClass.__call__ called.'
        return super(StandardClass, cls).__call__(*args, **kwargs)

# Instantiate class.
StandardClass()  # StandardClass.__call__ is not called!

Why metaclass's __call__ method called on class, but native class's __call__ not, when i instantiate a class?

2

There are 2 answers

5
warvariuc On BEST ANSWER

When you create a class instance, the metaclass's (whose instance the class is) __call__ is called.

# Instantiate class.
StandardClass()  # StandardClass.__call__ is not called!

When you create a class instance and then "call" the instance, then the class's __call__ is called. I don't think decorating __call__ with classmethod will work though.

This will call your metaclass's and class's __call__s:

StandardClass()()
0
BrenBarn On

Because magic methods are looked up on the class. In other words, when you write a __call__ method on a class, it defines what happens when you call an instance of that class, not what happens when you call the class itself. A class is an instance of its metaclass, so defining __call__ on the metaclass defines what happens when you call the class.

You can already see in your example that you're not doing the same thing in the two cases. For the metaclass example, you defined two things, a metaclass and a "meta-instance" (i.e., a regular class), and you called the second one. For the second example, you only defined one thing (a class). To be parallel to the first example, you would need to create an instance of StandardClass and then call that, in which case StandardClass.__call__ will be called.