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获取本机外网ip的方法
Apr 15 Python
以911新闻为例演示Python实现数据可视化的教程
Apr 23 Python
你所不知道的Python奇技淫巧13招【实用】
Dec 14 Python
win7上python2.7连接mysql数据库的方法
Jan 14 Python
Python如何读取MySQL数据库表数据
Mar 11 Python
Python+Pandas 获取数据库并加入DataFrame的实例
Jul 25 Python
python实现二维插值的三维显示
Dec 17 Python
Python从函数参数类型引出元组实例分析
May 28 Python
搭建python django虚拟环境完整步骤详解
Jul 08 Python
Python日志:自定义输出字段 json格式输出方式
Apr 27 Python
浅谈Python程序的错误:变量未定义
Jun 02 Python
pytorch中的torch.nn.Conv2d()函数图文详解
Feb 28 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 &amp;&amp; 逻辑与运算符使用说明
2010/03/04 PHP
AMFPHP php远程调用(RPC, Remote Procedure Call)工具 快速入门教程
2010/05/10 PHP
基于wordpress主题制作的具体实现步骤
2013/05/10 PHP
PHP错误机制知识汇总
2016/03/24 PHP
php安装ssh2扩展的方法【Linux平台】
2016/07/20 PHP
如何重写Laravel异常处理类详解
2020/12/20 PHP
prototype.js的Ajax对象
2006/09/23 Javascript
学习ExtJS(二) Button常用方法
2009/10/07 Javascript
JS和JQUERY获取页面大小,滚动条位置,元素位置(示例代码)
2013/12/14 Javascript
jquery select 设置默认选中的示例代码
2014/02/07 Javascript
node.js中的fs.truncateSync方法使用说明
2014/12/15 Javascript
jQuery+AJAX实现无刷新下拉加载更多
2015/07/03 Javascript
Vue入门之数据绑定(小结)
2018/01/08 Javascript
基于vue v-for 多层循环嵌套获取行数的方法
2018/09/26 Javascript
详解关于element el-button使用$attrs的一个注意要点
2018/11/09 Javascript
vue+Element中table表格实现可编辑(select下拉框)
2020/05/21 Javascript
Python使用scrapy采集数据时为每个请求随机分配user-agent的方法
2015/04/08 Python
python获取一组汉字拼音首字母的方法
2015/07/01 Python
使用python获取csv文本的某行或某列数据的实例
2018/04/03 Python
Python自动发送邮件的方法实例总结
2018/12/08 Python
Python paramiko模块使用解析(实现ssh)
2019/08/30 Python
python输入错误后删除的方法
2019/10/12 Python
详解Django CAS 解决方案
2019/10/30 Python
Mysql数据库反向生成Django里面的models指令方式
2020/05/18 Python
利用Vscode进行Python开发环境配置的步骤
2020/06/22 Python
python re.match()用法相关示例
2021/01/27 Python
深入浅出CSS3 background-clip,background-origin和border-image教程
2011/01/27 HTML / CSS
黑猩猩商店:The Chimp Store
2020/02/12 全球购物
Vinatis德国:法国领先的葡萄酒邮购公司
2020/09/07 全球购物
省三好学生申请材料
2014/01/22 职场文书
《雨霖铃》教学反思
2014/02/22 职场文书
工程技术员岗位职责
2015/04/11 职场文书
赢在中国观后感
2015/06/02 职场文书
年终工作总结范文
2019/06/20 职场文书
Mysql systemctl start mysqld报错的问题解决
2021/06/03 MySQL
nginx搭建NFS网络文件系统
2022/04/14 Servers