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. Go 每日一库之 go-homedir

    简介 今天我们来看一个很小,很实用的库go-homedir.顾名思义,go-homedir用来获取用户的主目录. 实际上,使用标准库os/user我们也可以得到这个信息: package main i ...

  2. vnpy源码阅读学习(3):学习vnpy的界面的实现

    学习vnpy的界面的实现 通过简单的学习了PyQt5的一些代码以后,我们基本上可以理解PyQt的一些用法,下面让我们来先研究下vnpy的UI部分的代码. 首先回到上一节看到的run.py(/vnpy/ ...

  3. 唬人的Java泛型并不难

    泛型 public interface Foo<E> {}public interface Bar<T> {}public interface Zar<?> {} ...

  4. python的range()

    range() 是一个函数,用来生成一个自然数的序列   用list()显示具体内容 扩展 # range()是一个函数,可以用来生成一个自然数的序列 r = range(5) # 生成一个这样的序列 ...

  5. 【一起学源码-微服务】Hystrix 源码三:Hystrix核心流程:Hystix降级、熔断等原理剖析

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲解了Hystrix在配合feign的过程中,一 ...

  6. Appium自动化测试框架研究(2)——搭建IOS环境

    今天的文章讲iOS的Appium环境搭建. 对于iOS而言,只能在Mac笔记本上安装Appium,以及所需要的各种组件. 也许有人会问,能否在Windows系统上使用Appium测试iOS手机,这不就 ...

  7. struts2 convention插件

    1.struts2自2.1以后推荐使用Convention Plugin支持struts零配置支持(引入jar:struts2-convention-plugin-2.x.x.jar)①convent ...

  8. python简易计算器

    import re """ 过程:(最内部的括号->先乘除,替换->整理表达式->加减)->替换 """ def m ...

  9. Django自定义状态码

    class BaseResponse: def __init__(self): self.code = 1000 self.data = None self.error = None @propert ...

  10. Http协议 Content-Type

    详情:https://www.cnblogs.com/ranyonsue/p/5984001.html *****Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面. ** ...