理解python中的元类(metaclass)

本文是根据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代码可以复制的。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页