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. # 曹工说Spring Boot源码(10)-- Spring解析xml文件,到底从中得到了什么(context:annotation-config 解析)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  2. python循环语句(while和for)

    循环语句分成两种,while循环 和 for循环 作用:可以使指定的代码块重复指定的次数 while循环: # 语法: # while 条件表达式 : # 代码块 # else : # 代码块 # 执 ...

  3. java实现FTP文件下载

    package com.vingsoft.util;/*** @author 作者:dujj* @version 创建时间:2020年1月13日 下午5:53:39*/import java.io.F ...

  4. Java入门 - 语言基础 - 07.修饰符

    原文地址:http://www.work100.net/training/java-modifier-type.html 更多教程:光束云 - 免费课程 修饰符 序号 文内章节 视频 1 概述 2 访 ...

  5. java异步调用方法

    一.利用多线程 直接new线程 Thread t = new Thread(){ @Override public void run() { longTimeMethod(); } }; 使用线程池 ...

  6. 图像矫正技术深入探讨(opencv)

    刚进入实验室导师就交给我一个任务,就是让我设计算法给图像进行矫正.哎呀,我不太会图像这块啊,不过还是接下来了,硬着头皮开干吧! 那什么是图像的矫正呢?举个例子就好明白了. 我的好朋友小明给我拍了这几张 ...

  7. 移动端ui框架

    https://blog.csdn.net/Robin_star_/article/details/81810197

  8. Kubernetes 服务自动发现CoreDNS

    前言 Service服务,是一个概念,逻辑通过selector标签代理指定后端pod.众所周知,pod生命周期短,状态不稳定,pod错误异常后新生成的Pod IP会发生变化,之前Pod的访问方式均不可 ...

  9. 浅谈DFS,BFS,IDFS,A*等算法

    搜索是编程的基础,是必须掌握的技能.--王主任 搜索分为盲目搜索和启发搜索 下面列举OI常用的盲目搜索: 1.dijkstra 2.SPFA 3.bfs 4.dfs 5.双向bfs 6.迭代加深搜索( ...

  10. Kafka网络模型和通信流程剖析

    1.概述 最近有同学在学习Kafka的网络通信这块内容时遇到一些疑问,关于网络模型和通信流程的相关内容,这里笔者将通过这篇博客为大家来剖析一下这部分内容. 2.内容 Kafka系统作为一个Messag ...