上一篇解决了通过调用类对象生成实例对象过程中可能遇到的命名空间相关的一些问题,这次我们向上回溯一层,看看类对象本身是如何产生的。
我们知道 type() 方法可以查看一个对象的类型,或者说判断这个对象是由那个类产生的:
print(type(12)) print(type('python')) class A: pass print(type(A))
通过这段代码可以看出,类对象 A 是由type() 产生的,也就是说 type 也可以用来产生新的对象,而且产生的是类对象,因此它是所有类对象的类:
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 为类的属性和方法:
class A: pass # 实际上等于 B = type('A', (), {}) print(A.__name__ == B.__name__) True
理论上说这就是元类的意义,但从实际的角度出发显然使用 class 语法更方便、合理,而元类的实际意义则是通过继承 type 类来构造一个新的元类,并进行特定的操作以产生具有特定行为的类对象。这样看来它的本质与普通的类对象没有差异,只不过继承的是 type 类。
在生成实例时是通过调用 __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: 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 为例,我们需要限制枚举类型中属性名称不得重复使用,则可以通过元类限制类的行为:
# 定义新的字典类,在赋值新的 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: 的原因是"TypeError catched") TypeError catched
Python 中一切皆为对象,所有的对象都是某一类的实例,或是某一元类的实例,type 是自己的元类也是自己的实例
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]