Python 类与元类的深度挖掘 II【经验】


Posted in Python onMay 06, 2016

上一篇解决了通过调用类对象生成实例对象过程中可能遇到的命名空间相关的一些问题,这次我们向上回溯一层,看看类对象本身是如何产生的。

我们知道 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: 的原因是?


print("TypeError catched")


TypeError catched

  Python 中一切皆为对象,所有的对象都是某一类的实例,或是某一元类的实例,type 是自己的元类也是自己的实例

Python 相关文章推荐
Python中IPYTHON入门实例
May 11 Python
Python中类型关系和继承关系实例详解
May 25 Python
Python函数式编程
Jul 20 Python
详解python Todo清单实战
Nov 01 Python
PyQt5基本控件使用详解:单选按钮、复选框、下拉框
Aug 05 Python
用python生成与调用cntk模型代码演示方法
Aug 26 Python
tensorflow自定义激活函数实例
Feb 04 Python
Python分析微信好友性别比例和省份城市分布比例的方法示例【基于itchat模块】
May 29 Python
Django REST Swagger实现指定api参数
Jul 07 Python
安装python依赖包psycopg2来调用postgresql的操作
Jan 01 Python
详解tf.device()指定tensorflow运行的GPU或CPU设备实现
Feb 20 Python
Python实现位图分割的效果
Nov 20 Python
Python 类与元类的深度挖掘 I【经验】
May 06 #Python
Python 迭代器工具包【推荐】
May 06 #Python
Python中内建函数的简单用法说明
May 05 #Python
Python使用Paramiko模块编写脚本进行远程服务器操作
May 05 #Python
Python环境下搭建属于自己的pip源的教程
May 05 #Python
使用Python判断质数(素数)的简单方法讲解
May 05 #Python
Python编程中归并排序算法的实现步骤详解
May 04 #Python
You might like
两种php去除二维数组的重复项方法
2015/11/04 PHP
thinkPHP框架动态配置用法实例分析
2018/06/14 PHP
Laravel 队列使用的实现
2019/01/08 PHP
JavaScript入门教程(2) JS基础知识
2009/01/31 Javascript
JQuery下关于$.Ready()的分析
2009/12/13 Javascript
toString()一个会自动调用的方法
2010/02/08 Javascript
基于jQuery的一个扩展form序列化到json对象
2010/12/09 Javascript
用显卡加速,轻松把笔记本打造成取暖器的办法!
2013/04/17 Javascript
在js文件中如何获取basePath处理js路径问题
2013/07/10 Javascript
JQuery中使用.each()遍历元素学习笔记
2014/11/08 Javascript
javascript二维数组转置实例
2015/01/22 Javascript
js实现宇宙星空背景效果的方法
2015/03/03 Javascript
jquery简单实现图片切换效果的方法
2015/05/12 Javascript
javascript Promise简单学习使用方法小结
2016/05/17 Javascript
jQuery实现div跟随鼠标移动
2020/08/20 jQuery
Vue.js实现价格计算器功能
2020/03/30 Javascript
详解vue表单——小白速看
2018/04/08 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
2018/04/13 Javascript
一文了解vue-router之hash模式和history模式
2019/05/31 Javascript
Vue替代marquee标签超出宽度文字横向滚动效果
2019/12/09 Javascript
js实现圆形显示鼠标单击位置
2020/02/11 Javascript
[13:16]INFAMOUS vs VGJ T BO3
2018/06/07 DOTA
Python实现简单截取中文字符串的方法
2015/06/15 Python
如何基于python实现年会抽奖工具
2020/10/20 Python
HTML5 和小程序实现拍照图片旋转、压缩和上传功能
2018/10/08 HTML / CSS
美国嘻哈文化生活方式品牌:GLD
2018/04/15 全球购物
Clarks鞋澳大利亚官方网站:Clarks Australia
2019/12/25 全球购物
大学生求职简历的自我评价
2013/10/14 职场文书
志愿者活动总结范文
2014/04/26 职场文书
2015年大学团支部工作总结
2015/05/13 职场文书
三八妇女节主持词
2015/07/04 职场文书
爱心捐赠活动简讯
2015/07/20 职场文书
MySQL查看表和清空表的常用命令总结
2021/05/26 MySQL
k8s部署redis cluster集群的实现
2021/06/24 Redis
DIV CSS实现网页背景半透明效果
2021/12/06 HTML / CSS
Win10多屏显示如何设置?Win10电脑多屏显示设置操作方法
2022/07/07 数码科技