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抓取网页图片并放到指定文件夹
Apr 24 Python
Python中使用logging模块代替print(logging简明指南)
Jul 09 Python
python通过pil为png图片填充上背景颜色的方法
Mar 17 Python
简介Django中内置的一些中间件
Jul 24 Python
python实现红包裂变算法
Feb 16 Python
python制作websocket服务器实例分享
Nov 20 Python
Python实现对一个函数应用多个装饰器的方法示例
Feb 09 Python
ubuntu安装sublime3并配置python3环境的方法
Mar 15 Python
Windows下将Python文件打包成.EXE可执行文件的方法
Aug 03 Python
Django中使用Celery的教程详解
Aug 24 Python
Python处理mysql特殊字符的问题
Mar 02 Python
判断Threading.start新线程是否执行完毕的实例
May 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
MySQL修改密码方法总结
2008/03/25 PHP
PHP在线生成二维码(google api)的实现代码详解
2013/06/04 PHP
jqgrid 表格数据导出实例
2013/11/21 Javascript
JavaScript中的typeof操作符用法实例
2014/04/05 Javascript
JavaScript中的style.cssText使用教程
2014/11/06 Javascript
7个让JavaScript变得更好的注意事项
2015/01/28 Javascript
JavaScript数组去重的五种方法
2015/11/05 Javascript
BootStrap的弹出框(Popover)支持鼠标移到弹出层上弹窗层不隐藏的原因及解决办法
2016/04/03 Javascript
AngularJS中比较两个数组是否相同
2016/08/24 Javascript
javascript 定时器工作原理分析
2016/12/03 Javascript
node.js+jQuery实现用户登录注册AJAX交互
2017/04/28 jQuery
Vue的MVVM实现方法
2017/08/16 Javascript
js实现手机web图片左右滑动效果
2017/12/29 Javascript
Vue中的v-for循环key属性注意事项小结
2018/08/12 Javascript
vue实现点击选中,其他的不选中方法
2018/09/05 Javascript
详解VSCode配置启动Vue项目
2019/05/14 Javascript
我要点爆”微信小程序云开发之项目建立与我的页面功能实现
2019/05/26 Javascript
vue路由守卫及路由守卫无限循环问题详析
2019/09/05 Javascript
修改layui的后台模板的左侧导航栏可以伸缩的方法
2019/09/10 Javascript
浅谈JavaScript中你可能不知道URL构造函数的属性
2020/07/13 Javascript
vue中用 async/await 来处理异步操作
2020/07/18 Javascript
python实现根据主机名字获得所有ip地址的方法
2015/06/28 Python
python executemany的使用及注意事项
2017/03/13 Python
Python生成器generator用法示例
2018/08/10 Python
Python遍历文件夹 处理json文件的方法
2019/01/22 Python
使用python画社交网络图实例代码
2019/07/10 Python
美国批发供应商:Kole Imports
2019/04/10 全球购物
新西兰最大的天然保健及护肤品网站:HealthPost(直邮中国)
2021/02/13 全球购物
运动会通讯稿500字
2014/02/20 职场文书
学生个人自我鉴定范文
2014/03/28 职场文书
公司晚会策划方案
2014/05/17 职场文书
党员个人整改措施
2014/10/24 职场文书
玄武湖导游词
2015/02/05 职场文书
经费申请报告
2015/05/15 职场文书
react合成事件与原生事件的相关理解
2021/05/13 Javascript
小程序实现筛子抽奖
2021/05/26 Javascript