4

After reading parts of Django source code, I want to do some test and write codes below to watch how metaclass works:

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

class AttrFiled(object): pass

class Test(MyMeta):
    name = AttrField()

It always complains:

TypeError: __new__() takes at least 2 arguments (1 given)

And I add modify it as below:

def with_metaclass(meta, *bases):  # copied from Django code.
    return meta("NewBase", bases, {})

class Test(with_metaclass(MyMeta)):
    name = CharField()

and it works.

I also read this What is a metaclass in Python?. But still feel confused.

Thanks in advance !

Community
  • 1
  • 1
oyjh
  • 1,248
  • 1
  • 9
  • 20

1 Answers1

4

with_metaclass was first introduced in the six library (if I recall correctly), that eases transition from Python 2 to Python 3. It's a clever trick to make code compatible with both of them.

Python 2 has the following syntax for declaring use of metaclass:

class Foo(object):
    __metaclass__ = FooMeta

Python 3, has a different one:

class Foo(metaclass=FooMeta):
    pass

What with_metaclass(meta) does: it creates an intermediate temporary class using meta metaclass' constructor directly, and you derive your class from it. So both Pythons -- 2 and 3 are happy.

You should also read Python documentation on this topic: http://docs.python.org/3/reference/datamodel.html#metaclasses


In your specific case, when you write class Test(MyMeta): you're just declaring a metaclass Test that is derived from MyMeta. To make a class, you need to write either

 class Test:
     __metaclass__ = MyMeta

or

 class Test(metaclass=MyMeta):
     ...

depending on what python version you have. Or, if you are not sure if you need to support Python 2 or not, you can simply use with_metaclass, it's also OK.

1st1
  • 1,101
  • 8
  • 8
  • Thanks! It's really helpful. BTW, why is it named with six.py ? 2*3 ? :) – oyjh Nov 22 '13 at 05:07
  • Quoting six' official documentation: "The name, “six”, comes from the fact that 2*3 equals 6. Why not addition? Multiplication is more powerful, and, anyway, “five” has already been snatched away by the Zope Five project." ;) – 1st1 Nov 22 '13 at 05:17