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})
Sign up to request clarification or add additional context in comments.

2 Comments

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 ah, got it. That's why you used type in the first place. I've updated the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.