Python 元类 - Metaclasses
- Python 元类 - Metaclasses
- 默认情况下儿, classes 是有 type() 构造的.
- 类的结构体在一个新的 namespace 被执行, 类的名字 class name 绑定(bound locally)到
- type(name, bases, namespace) 的结果上.
- 然而, 类的构造过程可以用户定义 - 在定义类的时候通过传入一个 metaclass 关键字;
- 或者通过继承至一个有 metaclass 关键字的父类.
- 如,
- class Meta(type):
- pass
- class MyClass(metaclass=Meta):
- pass
- or
- class MySubclass(MyClass):
- pass
- 在 class 定义中的其他关键字参数会被传给所有 metaclass 的操作.
- 当 class 构造体被执行的时候,python 解释器按照下列顺序 interpretation,
- 确定 class 对应的 metaclass ,
- 准备 class 的 namespace,
- 执行 class 定义体 - class body,
- 创建 class 对象 - class object.
- 下面我们逐一详细看一下儿以上几个解释步骤,
- metaclass 的确定 - Determining the appropriate metaclass
- metaclass 确定原则, 以 class B(metaclass = A, C) 为例阐述,
- 如果 class 的定义中没有指定基类 bases, 并且没有 metaclass 关键字,采用 type() 作为 metaclass.
- 如果有关键字 metaclass = A, 并且 A 不是 type() 的实例, 直接采用 A 作为 metaclass.
- 如果 A 是 type() 一个实例 instance, 或者有定义基类 bases, 则 采用 the most derived metaclass
- 注,
- candidate metaclasses 有2部分原来:
- a, metaclass 关键字, 如上例子中的 A
- b, 所有基类 bases的 metaclasses, 如上例子中的 type(c)
- the most derived metaclass 是 candidate metaclasses 集合中的一个,
- 它需要满足是所有其他的 candidate 的 subtype,如果没有任何一个 candidate 满足这个条件,
- 则 class 的定义会报错,raise TypeError exception.
- namespace 的确定 - Preparing the class namespace
- metaclass 确定了之后, 到了该确定 namespace 的时候.
- 如果 metaclass 有 __prepare__ attribute, 下面的语句将被调用,以确定 namespace
- namespace = metaclass.__prepare__(name, bases, **kwds),
- **kwds 来至于 class 的定义语句.
- 若 metaclass 没有 __prepare__ attribute, 则 类的 namespace 被初始化为空 an empty ordered mapping.
- class body 的执行 - Executing the class body
- class body 的执行, 可以用如下伪代码来表示,
- exec(body, globals(), namespace)
- 跟一般的对 exec()的调用的关键区别在于, 当 class 定义在一个函数中时候,
- 允许类的定义体 class body (包括其中的方法) 引用 '当前' 和 '外层' 作用域 scope 的 names
- "The key difference from a normal call to exec() is that lexical scoping allows
- the class body (including any methods) to reference names from the current and
- outer scopes when the class definition occurs inside a function."
- 然而,及时 class 被定义在一个函数中, 在 class scope 所定义的 names 对 class 中定义的 methods
- 仍然不可见.
- 对类变量 Class variables 的访问, 必须通过 self.variable 的形式访问,或作为第一个参数传入.
- 即,
- class A(object):
- a = "variable - a"
- def func1(self): # 必须通过 self.variable 的形式访问 Class variables
- print(self.a)
- def func2(self, a): # 作为第一个参数传入的形式访问 Class variables
- print(a)
- 再或者通过 隐式词法作用于 implicit lexically scoped __class__ 引用, 见下一节描述.
- class object 的创建 - Creating the class object
- class namespace 被确定之后, class object 由下面方法创建,
- metaclass(name, bases, namespace, **kwds)
- **kwds 跟传给 __prepare__ 的 **kwds, 即来至于 class 的定义语句.
- class object 即 super().__class__, 是一个在 class boby 中存在引用 __class__ 或 super 的方法
- 的情况下儿被编译器 compiler 创建一个隐式闭包引用 an implicit closure reference.
- 这保证了通过把 class 或者 instance 作为第一个参数来调用(属性/方法)的时候,无参数的 super()
- 可以正确的识别对应的 class, 即 class body 中的 self 机制.
- CPython implementation detail:
- 从 python 3.6 开始, __class__ 被传给 metaclass 作为 __classcell__ 存储在类的 namespace 中.
- 如果 __class__ 存在, 需要向上反推到 type.__new__ 的调用,以保证 class 的正确初始化.
- 如果 type.__new__ 调用失败,将报告 DeprecationWarning, 之后报 RuntimeWarning (python 3.6).
- 当采用默认 metaclass, 或某一 metaclass 调用了 type.__new__, 在创建了 class object 下面额外
- 的步骤姜维调用,
- a, type.__new__ 收集 class namespace 中的所有 descriptors, 定义 __set_name__() 方法
- b, 在所定义的类上调用 __set_name__ 中的描述符方法,
- class being defined and the assigned name of that particular descriptor 作为参数.
- c, 在被定义 class 的最近的基类上(MRO)调用 __init_subclass__(),
- 当 class object 被创建后, 如果 class 存在 decorators 将 class object 传给 decorators 装饰.
- 将返回的新对象绑定到 local namespace.
- 当一个新的 class 是由 type.__new__ 创建的, namespace parameter 复制一个新的 ordered mapping
- 中, 源对象被弃用. 新的 ordered mapping 将被包装成 read-only proxy, 最终成为 class object 的
- __dict__ 属性.
- 最后来看例子 - Metaclass example
- metaclasses 的潜在用途有很多, 包括 enum, logging, interface checking, automatic delegation,
- automatic property creation, proxies, frameworks, and automatic resource locking/synchronization.
- 下面是一个通过 collections.OrderedDict 来记录类参数定义顺序的 metaclasses 应用的例子,
- import collections
- class OrderedClass(type):
- @classmethod
- def __prepare__(metacls, name, bases, **kwds):
- return collections.OrderedDict()
- def __new__(cls, name, bases, namespace, **kwds):
- result = type.__new__(cls, name, bases, dict(namespace))
- result.members = tuple(namespace)
- return result
- class A(metaclass=OrderedClass):
- A = 1
- C = 2
- B = 3
- def one(self): pass
- def two(self): pass
- def three(self): pass
- def four(self): pass
- Output,
- >>> A.members
- ('__module__', '__qualname__', 'A', 'C', 'B', 'one', 'two', 'three', 'four')
- 当类 A 的定义体被执行的时候, 先调用 metaclass 的 __prepare__ 方法, __prepare__()
- 返回一个空的 collections.OrderedDict. 这个 OrderedDict 被用来按声明顺序记录 A 中
- 定义的 methods and attributes. 当解释器解释到 attributes 被声明的位置时, 通过执行声明
- 将 attributes 被添加到 ordered dictionary. 并且调用 metaclass 的 __new__() method 被,
- 结果就是 __new__() 创建一个新的的 type 并将 ordered dictionary 的 keys 存储在 members
- 属性中.
- Reference,
- python doc,
- https://docs.python.org/3/reference/datamodel.html#metaclasses
Python 元类 - Metaclasses的更多相关文章
- python类:描述器Descriptors和元类MetaClasses
http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...
- python元类:type和metaclass
python元类:type和metaclass python中一切皆对象,所以类本身也是对象.类有创建对象的能力,那谁来创建类的呢?答案是type. 1.用tpye函数创建一个类 class A(ob ...
- Python进阶丨如何创建你的第一个Python元类?
摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...
- python 元类
转载自 http://blog.jobbole.com/21351/ 类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大 ...
- [python]python元类
这两天在看Django框架,里面的filter实现原理搞不明白,最后发现跟python的元类有关系. 原文:http://stackoverflow.com/questions/100003/what ...
- Python元类实践--自己定义一个和collections中一样的namedtuple
大家可能很熟悉在collections模块中有一个很好用的扩展数据类型-namedtuple. 如果你还不知道这个类型,那么请翻看标准手册. 我利用元类轻松定义一个namedtuple. 先把代码贴上 ...
- python元类分析
刚開始接触到Python新式类中的元类的概念的时候非常是纠结了下..不知道这是个啥东西... 用下面几个定义来说明吧: (1)Python中,类也是对象..仅仅只是这样的对象比較的特殊,他用于创建别的 ...
- python元类理解2
恩,对元类理解又有新的收获,其实类似于装饰器,只不过装饰器是修饰函数,元类用来定制一个类. 代码如下,这是一个使用了函数做元类传递给类: input: def upper_attr(class_nam ...
- 3.python元类编程
1.1.propety动态属性 在面向对象编程中,我们一般把名词性的东西映射成属性,动词性的东西映射成方法.在python中他们对应的分别是属性self.xxx和类方法.但有时我们需要的属性需要根据 ...
随机推荐
- TensorFlow——tensorflow编程基础
0.tensorflow中的模型运行基础 tensorflow的运行机制属于定义和运行相分离,在操作层面可以抽象成两种:模型构建和模型运行. 在模型构建中的常见概念: 张量(tensor):数据,即某 ...
- c++快读与快输模板
快读 inline int read() { ; ; char ch=getchar(); ; ch=getchar();} )+(X<<)+ch-'; ch=getchar();} if ...
- Nginx的踩坑实录
1.昨天在为一个新项目配置地址转发,搞了很久都没生效,日志也没有问题,但就是没到转发的目标机器上. nginx.conf 配置如下: location /prism{ proxy_pass http: ...
- Java入门 - 面向对象 - 05.封装
原文地址:http://www.work100.net/training/java-encapsulation.html 更多教程:光束云 - 免费课程 封装 序号 文内章节 视频 1 概述 2 封装 ...
- 【Java并发基础】死锁
前言 我们使用加锁机制来保证线程安全,但是如果过度地使用加锁,则可能会导致死锁.下面将介绍关于死锁的相关知识以及我们在编写程序时如何预防死锁. 什么是死锁 学习操作系统时,给出死锁的定义为两个或两个以 ...
- prometheus和zabbix的对比
前言: 新公司要上监控,面试提到了Prometheus 是公司需要的监控解决方案,作为喜新厌旧的程序员,我当然是选择跟风了,之前主要做的是zabbix,既然公司需要prometheus,那没办法,只能 ...
- mysql使用唯一索引避免插入重复数据
使用MySQL 索引防止一个表中的一列或者多列产生重复值 一:介绍MYSQL唯一索引 如果要强烈使一列或多列具有唯一性,通常使用PRIMARY KEY约束. 但是,每个表只能有一个主键. 因此,如果使 ...
- 实验四:划分多个VLAN
1.配置图 2.配置命令 Switch1.Switch2.Switch3的配置是一样的,如下所示:(可直接复制交换机,可以只配置一次) 通过命令查看配置: Switch0的配置如下: 通过命令查看tr ...
- 学习Python中遇到的各种错误
错误列表 TypeError : 'moudle' object is not callable 错误:TypeError : 'moudle' object is not callable 代码: ...
- SpringBoot项目的parent依赖和配置文件*.properties、*.yml详解
1.idea创建SpringBoot项目 idea创建SpringBoot项目应该对很多人来说已经是菜到不能到菜的操作了,但是对于初学者小白来说,还是要讲解一下的.打开idea,然后选择Spring ...