本文是根据stackoverflow上关于“What are metaclasses in Python?”的回答翻译总结而成的。原数据连接:
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python
1. 类对象(class as object)
在了解元类(metaclass)之前, 首先得熟悉一下python中的类(class)。python对于什么是类有一个很独特的定义,借鉴与Smalltalk编程语言。
在很多编程语言中,类仅仅是一段用来描述如何产生对象的代码。python中的类也有这种功能。
>>>
class
ObjectCreator
(
object
):
...
pass
...
>>>
my_object
=
ObjectCreator
()
>>>
print
(
my_object
)
<
__main__
.
ObjectCreator
object at
0x8974f2c
>
但是,在python中,类的作用远不止这个。
类本身也是对象。
因为类是对象,所以我们可以对其进行很多操作:
- 赋值给一个变量
- 复制类
- 添加属性
- 作为参数传递给函数
>>>
class
ObjectCreator
(
object
):
...
pass
...
>>>
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
>
2. 动态创建类(Creating classes dynamically)
由于类是对象,所以你可以在任何地方创建它们,就像其他类型对象一样。
例如,在一个函数内通过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
<
class
'__main__.Foo'
>
>>>
print
(
MyClass
())
# you can create an object from this class
<
__main__
.
Foo
object at
0x89c6d4c
>
但是这并不是动态地创建类,因为你还是需要把类的整体写出来。
既然类是对象,那么它肯定是由什么东西生成的。这就引出了元类——用来创建类的‘东西’
3. 元类是一个类(class)对象
普通的类定义了该类的实例对象所具有的属性和方法,元类就定义了普通类所具有的属性和方法。即实例对象是普通类的实例化,普通类是元类的实例化。如下图所示:
Python中一切皆对象(object)。对象包括了数字(int), 字符串(string), 函数(functions)和类(class)。这些对象都需要通过调用元类来构建,所以元类通常被用作类工厂(class-factory)。 例如,我们通过调用类来创建类的实例,python中通过调用元类来创建一个新的类。结合__init__和__new__方法,元类就允许你在创建新类是添加一些额外的内容,甚至是完全重载该类。
除了类工厂外,元类还定义了类的类型(type),所以你可以用它来做更多的事情。例如,给元类定义一些常规的方法。元类中的方法(metaclass-method)就像是类方法(classmethod),所以你可以直接通过类名来调用这些方法,而不需要通过实例来调用。事实上,通过实例是无法调用元类中的方法的。这也是元类中方法与类方法的区别。
4. __metaclass__属性
当我们创建类时,可以在类的定义中添加__metaclass__属性。属性值是一些创建类的操作。
class Foo(object):
__metaclass__ = something...
[...]
添加完后,python就会用元类来创建类Foo。
注意,整个过程其实是这样的:
- 首先当你输入 class Foo(object)后,类Foo并没有在内存中被创建。python会在类Foo的定义中寻找__metaclass__。
- 如果找到了,则就会使用__metaclass__来创建类。
- 如果没有找到,就会调用type来创建类。
4. type
type是Python中常见的一个元类。你没有听错,type是一个类(以前我一直以为type是一个内置函数呢!),用来构建类对象。所以type(type)返回的是class type。类似的元类还有str和int, 分别用来构建字符串对象和整数对象。他们都有__class__属性,值为class ‘type’
>>> type(type)
<class 'type'>
>>> str.__class__
<class 'type'>
>>> int.__class__
<class 'type'>
除了返回对象的类型外,type还有一个截然不同的功能——动态创建类。type接受对类的描述作为参数,然后返回一个类。
例如:
>>>
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
>
type可以通过传入字典参数来定义类的属性。
>>>
Foo
=
type
(
'Foo'
,
(),
{
'bar'
:
True
})
上述代码等同于:
>>>
class
Foo
(
object
):
...
bar
=
True
type产生的类支持一切类的操作,包括继承。
>>>
print
(
Foo
)
<
class
'__main__.Foo'
>
>>>
print
(
Foo
.
bar
)
True
>>>
f
=
Foo
()
>>>
print
(
f
)
<
__main__
.
Foo
object at
0x8a9b84c
>
>>>
print
(
f
.
bar
)
True
>>>
FooChild
=
type
(
'FooChild'
,
(
Foo
,),
{})
等价于
>>>
class
FooChild
(
Foo
):
...
pass
5. 结语
类是对象,可以用来创建实例。而类本身又是元类的实例。
python中一切皆为对象,所有的对象都是类的实例或者元类的实例。
type是一个例外。type是其自身的元类。它是定义在解析器层面上的,不是你通过纯python代码可以复制的。