Python 什么是元类(metaclasses)?

1.什么是类

在理解元类以前,咱们必须先掌握Python中的类(class)。php

和大多数语言同样,Python中的类知识用来描述如何“生成一个对象”:python


可是,在Python中,类不只能用来描述如何生成一个对象,类自己也是对象数据库

在你使用关键词 class 的时候,Python就会执行它,并建立一个对象。数组

>>> class ObjectCreator(object):...  pass...


上述指令在内存中建立了一个“ObjectiveCreator”的对象。ruby

这个对象(类)自己具备建立对象(实例)的能力,所以它也是一个类。你能够对它作如下操做:bash

1.将其分配给变量
2.复制它
3.为其添加属性
4.将其做为函数参数传递微信

例如:app

2.动态建立类

因为类是对象,所以你能够像建立任何对象(数组、字典等)同样,随时随地建立类。函数

你甚至能够在函数里建立类:优化



可是,这样的类并非很动态,由于你必须本身编写整个类。

使用class关键字时,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(name, bases, attrs)


其中

  • name: 类名

  • bases: 元组,父类名

  • attrs: 字典,类属性值

所以你能够这样手动建立类:

>>> 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>


若是你想给它赋予属性,能够这样玩:

>>> class Foo(object):...  bar = True


等同于

>>> Foo = type('Foo', (), {'bar':True})


用来继承也是能够的:

>>> FooChild = type('FooChild', (Foo,), {})>>> print(FooChild)<class '__main__.FooChild'>>>> print(FooChild.bar) # bar is inherited from FooTrue


可见经过 type() 函数建立的类和直接写class是彻底同样的。

由于Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,而后调用 type() 函数建立出class。

正常状况下,咱们用class来定义类,可是,type()函数也容许咱们动态建立类,也就是说,动态语言自己支持运行期动态建立类,这和静态语言有很是大的不一样。

Python是经过什么作到这一切的?那就是元类。

3.什么是元类

元类就是用于建立类的“东西”。

你定义类是为了建立对象,Python中全部的类都是对象。元类是用于建立这些对象的。能够看这个例子:

MyClass = MetaClass()my_object = MyClass()


这有点像套娃。这段代码转化为type就是这样的:

MyClass = type('MyClass', (), {})


所以,咱们能够获得一个基本事实,type 自己就是一个元类

其实,就是 type 在幕后建立了Python中全部的类。

经过检查__class__属性,你会看到Python中,一切对象都是基于 type 的:

>>> age = 35>>> age.__class__<type 'int'>>>> name = 'bob'>>> name.__class__<type 'str'>>>> def foo(): pass>>> foo.__class__<type 'function'>>>> class Bar(object): pass>>> b = Bar()>>> b.__class__<class '__main__.Bar'>


那么,有个有趣的问题,__class__的__class__是什么呢?

>>> age.__class__.__class__<type 'type'>>>> name.__class__.__class__<type 'type'>>>> foo.__class__.__class__<type 'type'>>>> b.__class__.__class__<type 'type'>


所以,元类只是建立类对象的东西,若是愿意,能够将其称为“类的工厂”。

type 是Python使用的内置元类。不过,你能够建立本身的元类。

3.1 __metaclass__属性

在Python 2中,能够在编写类时添加属性__metaclass__,使用某个元类来建立该类:

class Foo(object): __metaclass__ = something... [...]


不过要当心的是,你虽然先写了 class Foo(object),但Foo这个对象还没有被建立,Python将先寻找__metaclass__类,找到后用它来建立Foo类。

若是没有这个__metaclass__类,它将使用 type 来建立类。

所以,类建立的流程是这样的:

1.建立的类中有__metaclass__元类属性吗?

2.若是有,那就用__metaclass__给该类在内存中建立一个类对象。

3.若是Python找不到__metaclass__,它将在MODULE级别查找__metaclass__属性 。

4.若是仍是没有,那就使用父类的元类来建立类对象。

如今的问题就是,你能够在__metaclass__中放置些什么代码呢?

答案就是:能够建立一个类的东西。那么什么能够用来建立一个类呢?type,或者任何继承或使用它的东西。

3.2 Python 3中的元类

设置元类的语法在Python3已改成:

class Foo(object, metaclass=something): ...


即再也不使用__metaclass__属性,而是在基类参数列表中引入关键字参数。

不过元类的基本工做方式不变。在Python3中,你能够将属性做为关键字参数传递给元类:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2): ...

4.为何须要元类

元类最主要的一个应用方向是建立API,一个最著名的应用是Django ORM,好比:

class Person(models.Model): name = models.CharField(max_length=30) age = models.IntegerField()


当你这样访问属性的时候:

person = Person(name='bob', age='35')print(person.age)


它并不会返回models.IntegerField,而是返回了一个整形的数字。

这是由于models.Model引用了一个ModelBase类,该类随后进行了魔术般地操做,使其可以与数据库字段进行挂钩。

这就是元类的做用,Django经过它,完成了系列复杂的幕后工做,将本来很是复杂的事情变得很是简单。

本文翻译自stackoverflow,有部分优化修改:
https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python

Python实用宝典 (pythondict.com)
不仅是一个宝典
欢迎关注公众号:Python实用宝典

本文分享自微信公众号 - Python实用宝典(pythondict)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。