总结一下(ps:如果这段看起来有点费劲,可以直接跳过看示例):

元类就是类的类,它的实例是类;

type() 并非函数,而是所有类的元类(至于为什么 type 要小写见这里,而 type 和 object 的关系,见“object 与 type”);

type() 既可以用于返回对象类型如:type(int),也可以用于类的创建如 :type(myList,List,{ }),事实上所有类的创建最终都是调用 type() 实现;

类创建时,python 首先会检测该类是否指定了元类,如果没有指定,则会去检测父类是否指定了元类…都没有找到则会检测该模块是否指定了元类,直至找到指定的元类,然后依据元类的定义创建并初始化该类(!!! 注意是创建的是该类,而不是该类的实例),不管元类如何定义,元类最终都会将自身以及其他指定的参数直接或间接的传递给 type() 创建该类 ;如果 python 最终未能找到指定的元类,便会将指定的参数直接传递给 type() 创建该类。

元类作为类的直接创建者,可以在类创建时拦截该类本身的创建程序,并可根据元类的意图创建并返回被修改后的类,因此,所有该类的实例都是被元类修改过的类的实例。这也是元类在应用中最大的价值!

Python 是动态语言,因此类只在该类作用域被创建时才会被创建,而元类则只会在该类被创建时才能做出响应,因此元类不会影响类的实例属性(因为实例属性只有在类实例创建时才会产生),元类也只能修改类的类属性,而不能修改该类的实例属性,但元类对类属性的修改,会影响到该类所有实例的属性,因为所有实例都是被修改过的类的实例。

事实上,被指定元类的类,其内定义的 new 和 init 是毫无意义的,因为该类不仅是通过元类创建的,也是通过元类实现初始化的。

示例:

class Human(type):
def __new__(cls, name, bases, dct):
attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
uppercase_attr = dict((name.upper(), value) for name, value in attrs)
return super(Human, cls).__new__(cls, name, bases, uppercase_attr)
class Person(metaclass = Human):
country = ''
"""Person class."""
def __init__(self, name, age):
 = name
self.age = age
def intro(self): #这里的 self 不要忘记,否则类实例无法调用
"""Return an introduction."""
print('who am I?')

上例中,Person 指定 Human 为其元类,一旦 Person 所在的作用域被执行,Human 会将创建 Person 类,并将 Person 类中所有非"__"开头的类属性标识符全部变为大写,并返回被修改后的 Person,当我们运行这段代码后:

runfile('C:/Users/xu_zh/.spyder2-py3/temp.py', wdir='C:/Users/...')
Person.country
Traceback (most recent call last):
File "", line 1, in 
Person.country
AttributeError: type object 'Person' has no attribute 'country'
Person.COUNTRY
Out[64]: ''
Person.INTRO
Out[65]:

Person 类中定义的 country 属性根本不存在,只存在被元类修改过的 COUNTRY,INTRO 方法也是如此,另外,我们再调用 init 方法看看:

Person.__init__

Out[66]:

可以发现完成 Person 类初始化的是 object 的 init 而非 Person 本身的 init,原因在于 Person 类是通过元类 Human 创建的,而 Human 并未定义 init 而是默认继承 object 的 init,因此 Person 类的创建就由 object 的 init 完成,如果此时我们按照 Person 本身的定义实例化一个 Person 对象,就会出现:

dobi = Person('dobi', 2)
Traceback (most recent call last):
File "", line 1, in 
dobi = Person('dobi', 2)
TypeError: object() takes no parameters

类型错误提示:object 对象无参数….

这样印证了 Person 类初始化程序是调用 object 对象的初始化程序实现的,那么 Person 类中定义的 name 和 age 等实例属性就是毫无意义的,它根本不可能存在于实例中:

dir(dobi)
Out[68]:
['COUNTRY',
'INTRO',
'__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__']
dobi.INTRO()
Out[69]: who am I?