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显示进度条的方法
Sep 20 Python
详解Django通用视图中的函数包装
Jul 21 Python
Python数据类型详解(四)字典:dict
May 12 Python
python设置值及NaN值处理方法
Jul 03 Python
python数据类型之间怎么转换技巧分享
Aug 20 Python
python根据时间获取周数代码实例
Sep 30 Python
Python参数传递实现过程及原理详解
May 14 Python
基于python代码批量处理图片resize
Jun 04 Python
python进度条显示-tqmd模块的实现示例
Aug 23 Python
Python字符串的15个基本操作(小结)
Feb 03 Python
Django后端按照日期查询的方法教程
Feb 28 Python
解决numpy和torch数据类型转化的问题
May 23 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
全国FM电台频率大全 - 11 浙江省
2020/03/11 无线电
php去除字符串中空字符的常用方法小结
2015/03/17 PHP
ThinkPHP中数据操作案例分析
2015/09/27 PHP
yii2.0实现创建简单widgets示例
2016/07/18 PHP
PHP类型约束用法示例
2016/09/28 PHP
laravel框架数据库操作、查询构建器、Eloquent ORM操作实例分析
2019/12/20 PHP
javascript 有趣而诡异的数组
2009/04/06 Javascript
javascript 匿名函数的理解(透彻版)
2010/01/28 Javascript
JS特殊函数(Function()构造函数、函数直接量)区别介绍
2013/05/19 Javascript
jquery实现手风琴效果实例代码
2013/11/15 Javascript
正则表达式中特殊符号及正则表达式的几种方法总结(replace,test,search)
2013/11/26 Javascript
详解js闭包
2014/09/02 Javascript
JavaScript中5种调用函数的方法
2015/03/12 Javascript
javascript实现点击商品列表checkbox实时统计金额的方法
2015/05/15 Javascript
详解jQuery中的元素的属性和相关操作
2015/08/14 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
AnglarJs中的上拉加载实现代码
2018/02/08 Javascript
JavaScript设计模式之职责链模式应用示例
2018/08/07 Javascript
使用layui 渲染table数据表格的实例代码
2018/08/19 Javascript
微信小程序Getuserinfo解决方案图解
2018/08/24 Javascript
Vue如何基于es6导入外部js文件
2020/05/15 Javascript
python self,cls,decorator的理解
2009/07/13 Python
Python合并多个装饰器小技巧
2015/04/28 Python
两个使用Python脚本操作文件的小示例分享
2015/08/27 Python
Python使用sftp实现上传和下载功能(实例代码)
2017/03/14 Python
10分钟用python搭建一个超好用的CMDB系统
2019/07/17 Python
Python3.6实现根据电影名称(支持电视剧名称),获取下载链接的方法
2019/08/26 Python
在django中自定义字段Field详解
2019/12/03 Python
Python3中configparser模块读写ini文件并解析配置的用法详解
2020/02/18 Python
意大利婴儿产品网上商店:Mukako
2018/10/14 全球购物
屈臣氏菲律宾官网:Watsons菲律宾
2020/06/30 全球购物
2014年党员公开承诺践诺书
2014/03/25 职场文书
会计人员演讲稿
2014/09/11 职场文书
2014年司机工作总结
2014/11/21 职场文书
《伯牙绝弦》教学反思
2016/02/16 职场文书
Hive常用日期格式转换语法
2022/06/25 数据库