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解析发往本机的数据包示例 (解析数据包)
Jan 16 Python
分析Python的Django框架的运行方式及处理流程
Apr 08 Python
深入解析Python中的list列表及其切片和迭代操作
Mar 13 Python
利用Python抓取行政区划码的方法
Nov 28 Python
浅谈Python在pycharm中的调试(debug)
Nov 29 Python
Python使用Pickle模块进行数据保存和读取的讲解
Apr 09 Python
使用Python OpenCV为CNN增加图像样本的实现
Jun 10 Python
python找出一个列表中相同元素的多个索引实例
Jun 11 Python
python实现UDP协议下的文件传输
Mar 20 Python
python使用梯度下降算法实现一个多线性回归
Mar 24 Python
Python通过kerberos安全认证操作kafka方式
Jun 06 Python
Django项目配置Memcached和Redis, 缓存选择哪个更有优势
Apr 06 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写的采集程序
2007/03/16 PHP
DedeCMS dede_channeltype表字段注释
2010/04/07 PHP
PHP对象Object的概念 介绍
2012/06/14 PHP
浅谈php错误提示及查错方法
2015/07/14 PHP
php编程中echo用逗号和用点号连接的区别
2016/03/26 PHP
用js+xml自动生成表格的东西
2006/12/21 Javascript
Document对象内容集合(比较全)
2010/09/06 Javascript
jQuery 阴影插件代码分享
2012/01/09 Javascript
jquery制作搜狐快站页面效果示例分享
2014/02/21 Javascript
浅谈Javascript中的Function与Object
2015/01/26 Javascript
详细介绍jQuery.outerWidth() 函数具体用法
2015/07/20 Javascript
jQuery Validate初步体验(二)
2015/12/12 Javascript
基于Vue.js实现简单搜索框
2020/03/26 Javascript
原生js实现倒计时--2018
2017/02/21 Javascript
jQuery实现贪吃蛇小游戏(附源码下载)
2017/03/04 Javascript
搭建简单的nodejs http服务器详解
2017/03/09 NodeJs
xmlplus组件设计系列之按钮(2)
2017/04/26 Javascript
jquery tmpl模板(实例讲解)
2017/09/02 jQuery
JS开发中基本数据类型具体有哪几种
2017/10/19 Javascript
关于node-bindings无法在Electron中使用的解决办法
2018/12/18 Javascript
Vue防止白屏添加首屏动画的实例
2019/10/31 Javascript
VueX模块的具体使用(小白教程)
2020/06/05 Javascript
微信小程序实现天气预报功能(附源码)
2020/12/10 Javascript
python基于multiprocessing的多进程创建方法
2015/06/04 Python
将Emacs打造成强大的Python代码编辑工具
2015/11/20 Python
Ubuntu下安装PyV8
2016/03/13 Python
Python socket模块实现的udp通信功能示例
2019/04/10 Python
python执行scp命令拷贝文件及文件夹到远程主机的目录方法
2019/07/08 Python
python中读入二维csv格式的表格方法详解(以元组/列表形式表示)
2020/04/24 Python
数据保密承诺书
2014/06/03 职场文书
小学班主任心得体会
2016/01/07 职场文书
离婚协议书格式范本
2016/03/18 职场文书
调解协议书范本
2016/03/21 职场文书
《风不能把阳光打败》读后感3篇
2020/01/06 职场文书
CSS3 制作的书本翻页特效
2021/04/13 HTML / CSS
微软官方消息,在 2023 年 4 月 11 日之后微软将不再为 Office 2013 和 Skype for Business 2015 提供安全更新
2022/04/21 数码科技