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创建只读属性对象的方法(ReadOnlyObject)
Feb 10 Python
Python MySQLdb模块连接操作mysql数据库实例
Apr 08 Python
python中zip和unzip数据的方法
May 27 Python
Python出现segfault错误解决方法
Apr 16 Python
Python实现感知机(PLA)算法
Dec 20 Python
详谈在flask中使用jsonify和json.dumps的区别
Mar 26 Python
Python实现矩阵相乘的三种方法小结
Jul 26 Python
Python面向对象程序设计之私有属性及私有方法示例
Apr 08 Python
pytorch使用Variable实现线性回归
May 21 Python
利用python实现汉字转拼音的2种方法
Aug 12 Python
python 如何调用远程接口
Sep 11 Python
使用Pytorch训练two-head网络的操作
May 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 随机生成10位字符代码
2009/03/26 PHP
php 变量定义方法
2009/06/14 PHP
php上传图片到指定位置路径保存到数据库的具体实现
2013/12/30 PHP
php获取当前url地址的方法小结
2017/01/10 PHP
Laravel框架实现定时发布任务的方法
2018/08/16 PHP
关于PhpStorm设置点击编辑文件自动定位源文件的实现方式
2020/12/30 PHP
js png图片(有含有透明)在IE6中为什么不透明了
2010/02/07 Javascript
关于Ext中form移除textfield方法:hide(),setVisible(false),remove()
2010/12/02 Javascript
js面向对象设计用{}好还是function(){}好(构造函数)
2011/10/23 Javascript
基于jquery自己写tab滑动门(通用版)
2012/10/30 Javascript
jquery删除指定的html标签并保留标签内文本内容的方法
2014/04/02 Javascript
学习JavaScript设计模式(封装)
2015/11/26 Javascript
ionic cordova一次上传多张图片(类似input file提交表单)的实现方法
2016/12/16 Javascript
微信小程序--onShareAppMessage分享参数用处(页面分享)
2017/04/18 Javascript
详谈AngularJs 控制器、数据绑定、作用域
2017/07/09 Javascript
浅谈react.js中实现tab吸顶效果的问题
2017/09/06 Javascript
JavaScript的Object.defineProperty详解
2018/07/09 Javascript
前端深入理解Typescript泛型概念
2020/03/09 Javascript
[03:55]DOTA2完美大师赛选手传记——LFY.MONET
2017/11/18 DOTA
pycharm 将django中多个app放到同个文件夹apps的处理方法
2018/05/30 Python
浅谈Django中view对数据库的调用方法
2019/07/18 Python
python打印9宫格、25宫格等奇数格 满足横竖斜相加和相等
2019/07/19 Python
Python PyInstaller库基本使用方法分析
2019/12/12 Python
利用纯CSS3实现tab选项卡切换示例代码
2016/09/21 HTML / CSS
英国的知名精品百货公司:House of Fraser(福来德)
2016/08/14 全球购物
四川成都导游欢迎词
2014/01/18 职场文书
幼儿园六一儿童节主持节目串词
2014/03/21 职场文书
关于学习的演讲稿
2014/05/10 职场文书
客户经理竞聘演讲稿
2014/05/15 职场文书
小学教师读书活动总结
2014/07/08 职场文书
房屋买卖协议书范本
2014/09/27 职场文书
五好家庭申报材料
2014/12/20 职场文书
超市收银员岗位职责
2015/04/07 职场文书
大学同学聚会感言
2015/07/30 职场文书
优质服务标语口号
2015/12/26 职场文书
python+opencv实现目标跟踪过程
2022/06/21 Python