类是为了创建对象,元类是为了创建类。
本文主要来自stackoverflow
在理解元类之前,我们需要掌握 python 中的类。在大多数语言中,类知识描述如何产生对象的代码段。
在python中也是如此:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
但是在python中,类的概念有了更多的扩展。还记得python中常说的 万物皆对象 嘛,python中类也是对象。
一旦使用关键字 class
, python 就会创建一个对象。代码如下:
>>> class ObjectCreator(object):
... pass
...
此时,会在内崔忠创建一个名称为 “ObjectCreator” 的对象。
这个对象(类)本身具有创建对象(实例)的能力,这也是为什么它是一个类
但是,它本质上来说,它仍然是一个对象。因此:
例如:
>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
... print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>
由于类是对象,因此你可以像创建任何对象一样即时创建它们。
首先,你可以使用 class 关键字在函数中创建一个类:
>>> def choose_class(name):
... if name == 'foo':
... class Foo(object):
... pass
... return Foo # return the class, not an instance
... else:
... class Bar(object):
... pass
... return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>
但是这不够动态,因为你让然必须自己编写整个类。
由于类是对象,因此它们必须有某种东西自动生成。
使用 class
关键字时, Python 会自动创建此对象。但是,与 Python 中的大多数事情一样,它为您提供了一种手动进行操作的方法。
还记得Python常用函数 type
吗?这个函数可以让你知道当前对象的类型:
>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>
其实,type
除了显示出当前对象的类型之外,它还可以动态的创建类。type
可以将类的描述作为参数,并返回一个类。
type
语法如下:
type(name of the class,
tuple of the parent class (for inheritance, can be empty),
dictionary containing attributes names and values)
例如:
>>> class MyShinyClass(object):
... pass
可以通过以下方式手动创建:
>>> MyShinyClass = type('MyShinyClass', (), {
}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>
可以看到,我们使用"MyShinyClass"作为类的名称和变量来保存类引用。
type
接受字典来定义类的属性。所以:
>>> class Foo(object):
... bar = True
可以翻译为:
>>> Foo = type('Foo', (), {'bar': True})
使用起来也和正常创建的类相同
>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True
当然,你也可以继承这个类。因此:
>>> class FooChild(Foo):
... pass
将会:
>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
>>> print(FooChild.bar) # bar is inherited from Foo
True
最后,如果你想要向类中添加方法。只需定义具有适当的函数并将其分配为所创建类的属性即可。
>> def echo_bar(self):
... print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True
在动态创建类之后,你可以添加更多的函数,就像将函数添加到正常创建的类对象中一样。
>>> def echo_bar_more(self):
... print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True
总结:在 Python 中,类是对象,你可以动态的创建类。
这也是 Python 在使用关键字 class
时所做的,并且通过使用元类来完成。
元类是创建 类 的东西
你定义类是为了创建对象。对吧?
但是我们知道,Python 中 类 也是对象。
元类就是创建 创建对象的类 的类。比较拗口。它们是银行的银行。你可以通过以下方式来描述它们:
MyClass = MetaClass()
my_object = MyClass()
从前面所述可知, type
可以执行如下操作:
MyClass = type('MyClass', (), {})
这是因为 type
函数实际上就是一个元类。 type
是 Python 用于来幕后创建所有类的元类。
你可能想知道为啥元类
type
为什么小写,我猜,可能是因为str
创建字符串,int
创建整数对象,为了和这些保持相同,毕竟,type
只是创建类对象的类
你可以通过__class__
属性来看到。
所有的,python所有的都是对象。其中包括整数,字符串,函数和类。它们都是对象。所有的这些都是从一个类创建的。
>>> age = 35
>>> age.__class__
>>> name = 'bob'
>>> name.__class__
>>> def foo(): pass
>>> foo.__class__
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
现在,__class__
的__class__
属性是什么呢?
>>> age.__class__.__class__
>>> name.__class__.__class__
>>> foo.__class__.__class__
>>> b.__class__.__class__
因此,元类只是创建类对象的东西。
如果愿意,可以称其为 ‘class factory’。
type
是Python 中内置的一个元类。当然,你也可以创建你自己的元类。
在Python2 中,你可以添加__metaclass__
属性当你在创建类的时候,
class Foo(object):
__metaclass__ = something...
[...]
如果你这样做了,Python 会使用元类去创建 Foo
这个类。
小心,这很棘手(实在不知道怎么翻译)
你首先写下代码class Foo(object)
,但是在内存中此时Foo
对象并未创建。
Python 会在类定义中寻找__metaclass__
,如果找到了,python会使用它创建这个对象即 类Foo
。 如果没有找到,Python 会使用 type
来创建这个类。
想想看,当你项如下这么操作时,python会做什么?
class Foo(Bar):
pass
Foo
中有__metaclass__
属性吗?
如果有,在内存中就会创建一个类对象(我说的是类对象,just follow me), 是使用 __metaclass__
创建的名为Foo
的类对象。
如果Python没有找到__metaclass__
。它将在模块级别中查找一个元类,并像上面那样做的一样。(但是这仅限于不继承任何内容的类,基本上他们属于旧式类)
这段不会翻译,附原文如下:If Python can’t find metaclass, it will look for a metaclass at the MODULE level, and try to do the same (but only for classes that don’t inherit anything, basically old-style classes).
如果 Python 没有找到任何元类,将会使用Bar
(第一个父类)的元类(它可能是type)来创建类对象。
在这类要小心的是,__metaclass__
属性不能够被继承,这个元类只能够是它的父类(Bar.__class__
)的。如果Bar
是通过type
的__metaclass__
属性来创建的,而不是type.__new__()
,那么子类将不会继承该行为。
看不懂,翻译的贼差劲
现在最大的问题是,你可以在 __metaclass__
中放进去什么?
答案是:可以创建类的东西
什么东西可以创建一个类? type
, 或者任何子类或者使用它的对象。
设置元类的方法在 python3 中已经更改。
class Foo(object, metaclass=something):
...
即不再使用
__metaclass__
属性,而是在基类列表中使用关键字参数。
但是元类的基本行为保持不变。
在python 3中添加到元类时,您可以将属性作为关键字参数传递给元类,如下所示:
class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):
...
Custom metaclasses
The main purpose of a metaclass is to change the class automatically, when it’s created.
You usually do this for APIs, where you want to create classes matching the current context.
Imagine a stupid example, where you decide that all classes in your module should have their attributes written in uppercase. There are several ways to do this, but one way is to set __metaclass__
at the module level.
This way, all classes of this module will be created using this metaclass, and we just have to tell the metaclass to turn all attributes to uppercase.
Luckily, __metaclass__
can actually be any callable, it doesn’t need to be a formal class (I know, something with ‘class’ in its name doesn’t need to be a class, go figure… but it’s helpful).
So we will start with a simple example, by using a function.
# the metaclass will automatically get passed the same argument
# that you usually pass to `type`
def upper_attr(future_class_name, future_class_parents, future_class_attr):
"""
Return a class object, with the list of its attribute turned
into uppercase.
"""
# pick up any attribute that doesn't start with '__' and uppercase it
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# let `type` do the class creation
return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
# but we can define __metaclass__ here instead to affect only this class
# and this will work with "object" children
bar = 'bip'
print(hasattr(Foo, 'BAR'))
# Out: False
print(hasattr(Foo, 'bar'))
# Out: True
f = Foo()
print(f.bar)
# Out: 'bip'
Now, let’s do exactly the same, but using a real class for a metaclass:
# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
# __new__ is the method called before __init__
# it's the method that creates the object and returns it
# while __init__ just initializes the object passed as parameter
# you rarely use __new__, except when you want to control how the object
# is created.
# here the created object is the class, and we want to customize it
# so we override __new__
# you can do some stuff in __init__ too if you wish
# some advanced use involves overriding __call__ as well, but we won't
# see this
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type(future_class_name, future_class_parents, uppercase_attr)
But this is not really OOP. We call type
directly and we don’t override or call the parent __new__
. Let’s do it:
class UpperAttrMetaclass(type):
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)
You may have noticed the extra argument upperattr_metaclass
. There is nothing special about it:__new__
always receives the class it’s defined in, as first parameter. Just like you have self for ordinary methods which receive the instance as first parameter, or the defining class for class methods.
Of course, the names I used here are long for the sake of clarity, but like for self, all the arguments have conventional names. So a real production metaclass would look like this:
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return type.__new__(cls, clsname, bases, uppercase_attr)
We can make it even cleaner by using super, which will ease inheritance (because yes, you can have metaclasses, inheriting from metaclasses, inheriting from type):
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)
Oh, and in python 3 if you do this call with keyword arguments, like this:
class Foo(object, metaclass=Thing, kwarg1=value1):
...
It translates to this in the metaclass to use it:
class Thing(type):
def __new__(cls, clsname, bases, dct, kwargs1=default):
...
That’s it. There is really nothing more about metaclasses.
The reason behind the complexity of the code using metaclasses is not because of metaclasses, it’s because you usually use metaclasses to do twisted stuff relying on introspection, manipulating inheritance, vars such as __dict__
, etc.
Indeed, metaclasses are especially useful to do black magic, and therefore complicated stuff. But by themselves, they are simple:
Since __metaclass__
can accept any callable, why would you use a class since it’s obviously more complicated?
There are several reasons to do so:
__new__
, __init__
and __call__
. Which will allow you to do different stuff. Even if usually you can do it all in __new__
, some people are just more comfortable using __init__
.Now the big question. Why would you use some obscure error prone feature?
Well, usually you don’t:
Metaclasses are deeper magic that 99% of users should never worry about. If you wonder whether you need them, you don’t (the people who actually need them know with certainty that they need them, and don’t need an explanation about why).
Python Guru Tim Peters
The main use case for a metaclass is creating an API. A typical example of this is the Django ORM.
It allows you to define something like this:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
But if you do this:
person = Person(name='bob', age='35')
print(person.age)
It won’t return an IntegerField
object. It will return an int, and can even take it directly from the database.
This is possible because models.Model
defines __metaclass__
and it uses some magic that will turn the Person you just defined with simple statements into a complex hook to a database field.
Django makes something complex look simple by exposing a simple API and using metaclasses, recreating code from this API to do the real job behind the scenes.
First, you know that classes are objects that can create instances.
Well in fact, classes are themselves instances. Of metaclasses.
>>> class Foo(object): pass
>>> id(Foo)
142630324
Everything is an object in Python, and they are all either instances of classes or instances of metaclasses.
Except for type
.
type
is actually its own metaclass. This is not something you could reproduce in pure Python, and is done by cheating a little bit at the implementation level.
Secondly, metaclasses are complicated. You may not want to use them for very simple class alterations. You can change classes by using two different techniques:
99% of the time you need class alteration, you are better off using these.
But 98% of the time, you don’t need class alteration at all.