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的更多相关文章

  1. python类:描述器Descriptors和元类MetaClasses

    http://blog.csdn.net/pipisorry/article/details/50444769 描述器(Descriptors) 描述器决定了对象属性是如何被访问的.描述器的作用是定制 ...

  2. python元类:type和metaclass

    python元类:type和metaclass python中一切皆对象,所以类本身也是对象.类有创建对象的能力,那谁来创建类的呢?答案是type. 1.用tpye函数创建一个类 class A(ob ...

  3. Python进阶丨如何创建你的第一个Python元类?

    摘要:通过本文,将深入讨论Python元类,其属性,如何以及何时在Python中使用元类. Python元类设置类的行为和规则.元类有助于修改类的实例,并且相当复杂,是Python编程的高级功能之一. ...

  4. python 元类

    转载自  http://blog.jobbole.com/21351/ 类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大 ...

  5. [python]python元类

    这两天在看Django框架,里面的filter实现原理搞不明白,最后发现跟python的元类有关系. 原文:http://stackoverflow.com/questions/100003/what ...

  6. Python元类实践--自己定义一个和collections中一样的namedtuple

    大家可能很熟悉在collections模块中有一个很好用的扩展数据类型-namedtuple. 如果你还不知道这个类型,那么请翻看标准手册. 我利用元类轻松定义一个namedtuple. 先把代码贴上 ...

  7. python元类分析

    刚開始接触到Python新式类中的元类的概念的时候非常是纠结了下..不知道这是个啥东西... 用下面几个定义来说明吧: (1)Python中,类也是对象..仅仅只是这样的对象比較的特殊,他用于创建别的 ...

  8. python元类理解2

    恩,对元类理解又有新的收获,其实类似于装饰器,只不过装饰器是修饰函数,元类用来定制一个类. 代码如下,这是一个使用了函数做元类传递给类: input: def upper_attr(class_nam ...

  9. 3.python元类编程

     1.1.propety动态属性 在面向对象编程中,我们一般把名词性的东西映射成属性,动词性的东西映射成方法.在python中他们对应的分别是属性self.xxx和类方法.但有时我们需要的属性需要根据 ...

随机推荐

  1. Js 数组按数量分部!

    使用 reduce 将数组分为几个部分,每个部分最多10个! 相比其他语言使用 js  实现这个逻辑非常的简单方便! var group = function (source, step) { if ...

  2. IDEA不编译空文件夹

    今天做项目的时候发现idea编译工程不会编译空文件夹,在resources下新建了个存储文件的空文件夹,编译后target里竟然没有,一直报空指针. 随便丢一个文件进去就行了,放一个demo.txt的 ...

  3. java.sql.SQLException: connection holder is null 问题处理

    问题描述 上上个周测试的时候突然报系统异常,于是我立即查看日志,发现是一个数据库异常:java.sql.SQLException: connection holder is null我第一想到的就是可 ...

  4. 「扫盲」Elasticsearch

    前言 只有光头才能变强. 文本已收录至我的GitHub精选文章,欢迎Star:https://github.com/ZhongFuCheng3y/3y 不知道大家的公司用Elasticsearch多不 ...

  5. 异数OS TCP协议栈测试(三)--长连接篇

    异数OS TCP协议栈测试(三)--长连接篇 本文来自异数OS社区 github:   异数OS-织梦师(消息中间件)群: 476260389 异数OS TCP长连接技术简介 说起长连接,则首先要谈对 ...

  6. spring boot 整合 swagger2

    swagger2为了更好的管理api文档接口 swagger构建的api文档如下,清晰,避免了手写api诸多痛点 一,添加依赖 <!--swagger2的官方依赖--> <depen ...

  7. 【javaWeb】HttpServletRequest常用获取URL的方法

    1.request.getRequestURL() 返回的是完整的url,包括Http协议,端口号,servlet名字和映射路径,但它不包含请求参数. 2.request.getRequestURI( ...

  8. 树莓派通过模数转换芯片ADC0832读取LM35温度传感器数据

    树莓派通过模数转换芯片ADC0832读取LM35温度传感器数据 今天和小朋友一起玩树莓派,打算来做一个测量室温的小实验.经过几个小时的研究和测试,终于能够成功读取LM35传感器的温度数据了.本文主要记 ...

  9. CF990G

    题意 https://codeforces.com/contest/990/problem/G 思考 在200000以内,因数个数最多的数位166320,共有160个因数.可以知道,从一个节点向下走最 ...

  10. Web自动化测试项目(四)测试报告

    测试报告生成 使用HTMLTestRunner 生成测试报告 本文使用的 HTMLTestRunner 来源于github: https://github.com/githublitao/HTMLTe ...