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 转换 Javascript %u 字符串为python unicode的代码
Sep 06 Python
利用python 更新ssh 远程代码 操作远程服务器的实现代码
Feb 08 Python
python使用pygame框架实现推箱子游戏
Nov 20 Python
python判断字符串或者集合是否为空的实例
Jan 23 Python
Python selenium根据class定位页面元素的方法
Feb 26 Python
numpy数组之存取文件的实现示例
May 24 Python
pyenv与virtualenv安装实现python多版本多项目管理
Aug 17 Python
Python自动生成代码 使用tkinter图形化操作并生成代码框架
Sep 18 Python
pytorch 数据处理:定义自己的数据集合实例
Dec 31 Python
Python-numpy实现灰度图像的分块和合并方式
Jan 09 Python
keras 如何保存最佳的训练模型
May 25 Python
python中if及if-else如何使用
Jun 02 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/01/28 PHP
PHP控制前台弹出对话框的实现方法
2016/08/21 PHP
PHP curl 或 file_get_contents 获取需要授权页面的方法
2017/05/05 PHP
基于JQuery的访问WebService的代码(可访问Java[Xfire])
2010/11/19 Javascript
juqery 学习之五 文档处理 插入
2011/02/11 Javascript
jQuery ReferenceError: $ is not defined 错误的处理办法
2013/05/10 Javascript
jQuery动画效果-fadeIn fadeOut淡入浅出示例代码
2013/08/28 Javascript
jQuery实现动画效果circle实例
2015/08/06 Javascript
老生常谈js动态添加事件--- 事件委托
2016/07/19 Javascript
浅谈JS中的bind方法与函数柯里化
2016/08/10 Javascript
DropDownList控件绑定数据源的三种方法
2016/12/24 Javascript
基于JavaScript实现类名的添加与移除
2017/04/23 Javascript
css和js实现弹出登录居中界面完整代码
2017/11/26 Javascript
微信小程序实现的日期午别医生排班表功能示例
2019/01/09 Javascript
Nuxt.js实战和配置详解
2019/08/05 Javascript
node 解析图片二维码的内容代码实例
2019/09/11 Javascript
对Python信号处理模块signal详解
2019/01/09 Python
Python地图绘制实操详解
2019/03/04 Python
python 将有序数组转换为二叉树的方法
2019/03/26 Python
使用Python给头像戴上圣诞帽的图像操作过程解析
2019/09/20 Python
python 3.8.3 安装配置图文教程
2020/05/21 Python
Python制作一个仿QQ办公版的图形登录界面
2020/09/22 Python
Mixbook加拿大:照片书,照片卡,剪贴簿,年历和日历
2017/02/21 全球购物
德国价格合理的品牌商品购物网站:averdo
2019/03/21 全球购物
欧洲、亚洲、非洲和拉丁美洲的度假套餐:Great Value Vacations
2019/03/30 全球购物
历史学专业毕业生求职信
2013/09/27 职场文书
求职推荐信
2013/10/28 职场文书
自主招生自荐信指南
2014/02/04 职场文书
大学生两会学习心得体会
2014/03/10 职场文书
唐山大地震观后感
2015/06/05 职场文书
爱国主义教育基地观后感
2015/06/18 职场文书
2016护理专业求职自荐书
2016/01/28 职场文书
八年级语文教学反思
2016/03/03 职场文书
Python如何把不同类型数据的json序列化
2021/04/30 Python
vue.js Router中嵌套路由的实用示例
2021/06/27 Vue.js
Python OpenCV实现图像模板匹配详解
2022/04/07 Python