views:

132

answers:

3

I was writing a metaclass and accidentally did it like this:

class MetaCls(type):
    def __new__(cls, name, bases, dict):
        return type(name, bases, dict)

...instead of like this:

class MetaCls(type):
    def __new__(cls, name, bases, dict):
        return type.__new__(cls, name, bases, dict)

What exactly is the difference between these two metaclasses? And more specifically, what caused the first one to not work properly (some classes weren't called into by the metaclass)?

A: 

It's all described pretty well here.

If you don't return the right type of object, there's no point to defining a custom metaclass.

Azeem.Butt
But that's the question I'm getting at: why doesn't `type` return the right kind of object? I know that I have to use the subclass's `__new__` method, but how is that different from using `type`?
Jason Baker
How is type supposed to magically know what kind of object you want it to make?
Azeem.Butt
@NSD - well, because I tell it. I passed in name, bases and dict. What other piece of information do I need to give it?
Jason Baker
+2  A: 
return type(name, bases, dict)

What you get back from this is a new type, and not a MetaCls instance at all. Consequently, your methods defined in MetaCls (including __init__) can't ever be called.

type.__new__ will be called as part of creating that new type, yes, but the value of cls going into that function is going to be type and not MetaCls.

bobince
+1  A: 

In the first example you're creating a whole new class:

>>> class MetaA(type):
...     def __new__(cls, name, bases, dct):
...         print 'MetaA.__new__'
...         return type(name, bases, dct)
...     def __init__(cls, name, bases, dct):
...         print 'MetaA.__init__'
... 
>>> class A(object):
...     __metaclass__ = MetaA
... 
MetaA.__new__
>>> 

while in the second case you're calling parent's __new__:

>>> class MetaA(type):
...     def __new__(cls, name, bases, dct):
...         print 'MetaA.__new__'
...         return type.__new__(cls, name, bases, dct)
...     def __init__(cls, name, bases, dct):
...         print 'MetaA.__init__'
... 
>>> class A(object):
...     __metaclass__ = MetaA
... 
MetaA.__new__
MetaA.__init__
>>> 
mg