1

Code is better than words here:

class MetaA(type):
    def __new__(cls, name, bases, attrs):
        print "MetaA"
        return super(MetaA, cls).__new__(cls, name, bases, attrs)


class A(object):
    __metaclass__ = MetaA

This will print MetaA

class MetaB(MetaA):
    def __new__(cls, name, bases, attrs):
        print "MetaB"
        return super(MetaB, cls).__new__(cls, name, bases, attrs)


B = type('B', (A, ), {'__metaclass__': MetaB})

This will print MetaA again (?!)

I would expect:

MetaB
MataA

The question are:

  1. why I'm getting MetaA only?
  2. How to change the code to get:

    MetaB
    MataA
    

1 Answer 1

2

The reason is that type(name, bases, dict) is not the right way to create class with specified metaclass.

Interpreter actually avoids calling type(name, bases, dict) when in sees __metaclass__ attribute defined and calls mateclass instead of type(name, bases, dict) as it would normally do.

Here is the relevant docs section that explains it:

When the class definition is read, if __ metaclass __ is defined then the callable assigned to it will be called instead of type(). This allows classes or functions to be written which monitor or alter the class creation process [...]

If you modify your code like this:

class MetaA(type):
    def __new__(cls, name, bases, attrs):
        print "MetaA"
        return super(MetaA, cls).__new__(cls, name, bases, attrs)

class A(object):
    __metaclass__ = MetaA


class MetaB(MetaA):
    def __new__(cls, name, bases, attrs):
        print "MetaB"
        return super(MetaB, cls).__new__(cls, name, bases, attrs)

class B(A):
    __metaclass__ = MetaB

... then you'll get the expected output:

MetaA
MetaB 
MetaA 

(first line printed when creating class A, second and third when creating class B)

UPDATE: The question assumed dynamic creation of the class B, so I'll extend my answer.

To construct the class B with metacalss dynamically you should do the same thing as interpreter does, i.e. construct class with metaclass in __metaclass__ instad of type(name, bases, dict).

Like this:

B = MetaB('B', (A, ), {'__metaclass__': MetaB})
2
  • You are partially right, but you missed the point. The reason I'm using 'type' is because I have lots of classes I want to create dynamically. But since 'type' is metaclass should write MetaB('B', (A, ), {'metaclass': MetaB}) instead of type('B', (A, ), {'metaclass': MetaB}). I've figured it out yesterday. If you change your answer to reflect my requirements (classes created dynamically) I'll accept you answer.
    – mnowotka
    Commented May 22, 2013 at 8:22
  • 1
    @mnowotka ah, got it. That's why you used type in the first place. I've updated the answer.
    – Eugene Loy
    Commented May 22, 2013 at 8:39

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.