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中的socket模块使用代理实例
May 29 Python
自己使用总结Python程序代码片段
Jun 02 Python
Python编程使用NLTK进行自然语言处理详解
Nov 16 Python
Python3实现取图片中特定的像素替换指定的颜色示例
Jan 24 Python
Python数据类型之List列表实例详解
May 08 Python
Django实现auth模块下的登录注册与注销功能
Oct 10 Python
Django admin禁用编辑链接和添加删除操作详解
Nov 15 Python
python开发实例之python使用Websocket库开发简单聊天工具实例详解(python+Websocket+JS)
Mar 18 Python
python 实现图片批量压缩的示例
Dec 18 Python
详解Python+Selenium+ChromeDriver的配置和问题解决
Jan 19 Python
python 详解turtle画爱心代码
Feb 15 Python
Pandas数据结构之Series的使用
Mar 31 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
WordPress中用于更新伪静态规则的PHP代码实例讲解
2015/12/18 PHP
编写跨浏览器的javascript代码必备[js多浏览器兼容写法]
2008/10/29 Javascript
关于IE7 IE8弹出窗口顶上
2008/12/22 Javascript
用于判断用户注册时,密码强度的JS代码
2009/01/01 Javascript
xml和web特殊字符
2009/04/28 Javascript
js实现无需数据库的县级以上联动行政区域下拉控件
2013/08/14 Javascript
js判断元素是否隐藏的方法
2014/06/09 Javascript
滚动条响应鼠标滑轮事件实现上下滚动的js代码
2014/06/30 Javascript
简介JavaScript中的getUTCFullYear()方法的使用
2015/06/10 Javascript
百度地图API之本地搜索与范围搜索
2015/07/30 Javascript
ES6所改良的javascript“缺陷”问题
2016/08/23 Javascript
jQuery实现弹出窗口弹出div层的实例代码
2017/01/09 Javascript
js清除浏览器缓存的几种方法
2017/03/15 Javascript
JS正则替换去空格的方法
2017/03/24 Javascript
详解Angular2中Input和Output用法及示例
2017/05/21 Javascript
Iphone手机、安卓手机浏览器控制默认缩放大小的方法总结(附代码)
2017/08/18 Javascript
jquery实现左右轮播切换效果
2018/01/01 jQuery
nodejs结合socket.io实现websocket通信功能的方法
2018/01/12 NodeJs
vue 数据遍历筛选 过滤 排序的应用操作
2020/11/17 Javascript
python实现汉诺塔递归算法经典案例
2021/03/01 Python
Pyqt实现无边框窗口拖动以及窗口大小改变
2018/04/19 Python
Python之修改图片像素值的方法
2019/07/03 Python
python-地图可视化组件folium的操作
2020/12/14 Python
HTML5新增的标签和属性归纳总结
2018/05/02 HTML / CSS
草莓网化妆品澳大利亚站:Strawberrynet AU
2017/12/18 全球购物
澳大利亚拥有最好的家具和家居用品在线目的地:Nestz
2019/02/23 全球购物
微软加拿大官方网站:Microsoft Canada
2019/04/28 全球购物
Camille Jewelry官网:现代女性时尚首饰
2019/07/07 全球购物
澳大利亚著名的纺织品品牌:Canningvale
2020/05/05 全球购物
财务人员个人自荐信范文
2013/09/26 职场文书
心碎乌托邦的创业计划书范文
2013/12/26 职场文书
宗教学大学生职业生涯规划范文
2014/02/08 职场文书
班组建设经验交流材料
2014/05/12 职场文书
聚众斗殴罪辩护词
2015/05/21 职场文书
为自己工作观后感
2015/06/11 职场文书
用Python可视化新冠疫情数据
2022/01/18 Python