上一篇解决了经过调用类对象生成实例对象过程当中可能遇到的命名空间相关的一些问题,此次咱们向上回溯一层,看看类对象自己是如何产生的。python
咱们知道 type()
方法能够查看一个对象的类型,或者说判断这个对象是由那个类产生的:编程
print(type(12)) print(type('python'))
<class 'int'> <class 'str'>
class A: pass print(type(A))
<class 'type'>
经过这段代码能够看出,类对象 A
是由type()
产生的,也就是说 type
也能够用来产生新的对象,并且产生的是类对象,所以它是全部类对象的类:app
print(type.__doc__)
type(object_or_name, bases, dict) type(object) -> the object's type type(name, bases, dict) -> a new type
class
定义类的语法实际上转化为 type(name, bases, dict)
,其中 name
参数为类的名字,bases
为继承父类的元组,dict
为类的属性和方法:code
class A: pass # 实际上等于 B = type('A', (), {}) print(A.__name__ == B.__name__)
True
理论上说这就是元类的意义,但从实际的角度出发显然使用 class
语法更方便、合理,而元类的实际意义则是经过继承 type
类来构造一个新的元类,并进行特定的操做以产生具备特定行为的类对象。这样看来它的本质与普通的类对象没有差别,只不过继承的是 type
类。orm
在生成实例时是经过调用 __init__
方法进行初始化的,而实际上在此以前会先调用 __new__
方法用于建立实例,再经过 __init__
初始化,就好像 __new__
负责声明变量,而 __init__
负责对声明的变量进行初始化同样。这里有一个规则是 __new__(cls,)
的返回值必须是 cls
参数的实例,不然 __init__
将不会触发,例如在 enum.Enum
的定义中,因为枚举类型是单例模式,所以在定义 __new__
的时候没有返回其实例,也就不会进行初始化:对象
class Enum: def __new__(cls, value): print(cls, value) return value def __init__(self): print("Will not be called!") e = Enum(1)
<class '__main__.Enum'> 1
一般状况下本身定义 __new__
须要经过调用父类的 __new__
方法建立一个 cls
的实例,一样在定义元类的时候则是调用上面提到的 type
的用法(由于元类继承自 type
):继承
class MetaEnum(type): def __new__(metaclass, name, base, attrs): print("Metaclass: {}\nName: {}\nParents: {}\nAttributes: {}".format(metaclass, name, base, attrs)) return super().__new__(metaclass, name, base, attrs)
class Enum(metaclass=MetaEnum): # Python 2.7 中定义元类的方法是使用 __metaclass__ 变量 # [PEP 3115](https://www.python.org/dev/peps/pep-3115/) # 将 Python 3.0 之后语法改成 class Cls(metaclass=Meta) test = 0
Metaclass: <class '__main__.MetaEnum'> Name: Enum Parents: () Attributes: {'__qualname__': 'Enum', '__module__': '__main__', 'test': 0}
此时咱们再来看 Enum
的类,已经再也不是 type
而是其元类 MetaEnum
:图片
type(Enum)
__main__.MetaEnum
除了 __new__
方法以外,PEP 3115 还定义了 __prepare__
属性,用于设定初始化的命名空间(即 type
的第 3 个参数),仍是以 enum.Enum
为例,咱们须要限制枚举类型中属性名称不得重复使用,则能够经过元类限制类的行为:get
# 定义新的字典类,在赋值新的 dict[k] = v 时 # 检查 k 是否重复 class _EnumDict(dict): def __init__(self): super().__init__() self.members = [] def __setitem__(self, k, v): if k in self.members: raise TypeError("Attempted to reuse key: '{}'".format(k)) else: self.members.append(k) super().__setitem__(k, v) class MetaEnum(type): @classmethod def __prepare__(metaclass, cls, bases): return _EnumDict() def __new__(metaclass, name, base, attrs): return super().__new__(metaclass, name, base, attrs) class Enum(metaclass=MetaEnum): pass class Color(Enum): try: red = 1 red = 2 except TypeError:# 这里没有使用 as err: 的缘由是? print("TypeError catched")
TypeError catched
Python 中一切皆为对象,全部的对象都是某一类的实例,或是某一元类的实例,type
是本身的元类也是本身的实例:it
元类在 Python 中属于比较深层的黑魔法,在通常的平常应用中可能并不经常使用,但理解其背后的原理对于理解 Python 面向对象编程以及一切皆为对象的理念颇有帮助;若是你须要对类进行深度改造,至少要知道从何入手。