python中的元类Metaclass

理解元类之前需要学习的知识

如果说让我们创建一个类,最先想到的肯定是用class创建,当我们使用class创建类的时候,python解释器自动创建这个对象,但是python同样也提供了手动处理的方法来创建类,这就是用python的自建函数type()

我们所熟知的type()函数的作用是返回一个参数的类型,但是实际上,它也有一种完全不同的能力,即接受一个类的一些描述作为参数,然后返回一个类。

type()函数的语法是这样的:

type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))

举个例子:

class ReedSun(ShuaiGe):shuai = Truedef test(x):return x+2
# 就等价于
type("ReedSun", (ShuaiGe,), {"shuai":True, "test":lambda x: x+2})
# (属性和方法本质上都是方法)

在python中,类也是对象,当我们使用class关键词创建一个类的时候,Python解释器仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。

元类是什么

元类是什么?元类实际上就是用来创建类的东西。为了帮助我们理解,我们可以这样想,我们创建类就是为了创建类的实例,同样的,我们创建元类就是为了创建类。元类就是类(实例)的类,就像下面这样

Metaclass() = class
class() = object  # object==>实例

理解了什么是元类,我们再来看一看type()函数。

其实type就是一个元类,type就是我们用来创建所有的类的元类。(如果我们要创建自己定义的元类的话,也要从type中继承)

元类的工作原理

我们来看一下下面这个例子

class ReedSunMetaclass(type):passclass Foo(object, metaclass = ReedSunMetaclass): passclass Bar(Foo):pass
  1. 首先,我们创建了一个元类ReedSunMetaclass(注意!按照默认习惯,元类的类名总是以Metaclass结尾,以便清楚地表示这是一个元类)

  2. 然后,我们又用元类ReedSunMetaclass创建了一个Foo类,(同时,Foo类的属性__metaclass__就变成了ReedSunMetaclass)

  3. 最后,我们创建了一个子类Bar继承自Foo

我们来试着理解一下在python内部是怎么执行这几个步骤的:

  • 对于父类Foo,Python会在类的定义中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Foo,如果没有找到,就会用内建的type来创建这个类。很显然,它找到了。

  • 对于子类Bar, python会先在子类中寻找__metaclass__属性,如果找到了,Python就会用它来创建类Bar,如果没有找到,就再从父类中寻找,直到type。显然,它在父类中找到了。

我们可以看到使用元类的一个好处了,即他可以让子类隐式的继承一些东西。

自定义元类

元类的主要目的就是为了当创建类时能够自动地改变类。创建类我们需要定义__new__()函数,__new__ 是在__init__之前被调用的特殊方法,是用来创建对象并返回之的方法。我们举个例子来说明定义自定义元类的方法。

__new__()方法接收到的参数依次是:
1. 当前准备创建的类的对象;
2. 类的名字;
3. 类继承的父类集合;
4. 类的方法集合。

class ReedSunMetaclass(type):def __new__(cls, name, bases, attrs):# 添加一个属性attrs['哈哈哈'] = Truereturn type.__new__(cls, name, bases, attrs)

我们用一个实际例子来说明元类的用法

ORM就是一个典型的使用元类的例子。ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简单,不用直接操作SQL语句。下面我就用这个ORM的例子来说明一下元类的用法。

#ORM:object relational mapping 对象-关系映射
#把关系数据库的一行映射为一个对象,也就是一个类对应一个表
#ORM框架所有的类只能动态定义# 定义Field(定义域:元类遇到Field的方法或属性时即进行修改)
class Field(object):def __init__(self, name, column_type):  # column==>列类型self.name = nameself.column_type = column_type# 当用print打印输出的时候,python会调用他的str方法# 在这里是输出<类的名字,实例的name参数(定义实例时输入)># 在ModelMetaclass中会用到def __str__(self):return "<%s:%s>" % (self.__class__.__name__, self. name)  # __class__获取对象的类,__name__取得类名# 进一步定义各种类型的Field
class StringField(Field):def __init__(self, name):# super(type[, object-or-type])  返回type的父类对象# super().__init()的作用是调用父类的init函数# varchar(100)和bigint都是sql中的一些数据类型super(StringField, self).__init__(name, "varchar(100)")  class IntegerField(Field):def __init__(self, name):super(IntegerField, self).__init__(name, "bigint")# 编写ModelMetaclass
class ModelMetaclass(type):# __new__方法接受的参数依次是:# 1.当前准备创建的类的对象(cls)# 2.类的名字(name)# 3.类继承的父类集合(bases)# 4.类的方法集合(attrs)def __new__(cls, name, bases, attrs):# 如果说新创建的类的名字是Model,那直接返回不做修改if name == "Model":return type.__new__(cls, name, bases, attrs)print("Found model:%s" % name)mappings = dict()for k, v in attrs.items():if isinstance(v, Field):print("Found mappings:%s ==> %s" % (k, v))  # 找到映射, 这里用到上面的__str__mappings[k] = v# 结合之前,即把之前在方法集合中的零散的映射删除,# 把它们从方法集合中挑出,组成一个大方法__mappings__# 把__mappings__添加到方法集合attrs中for k in mappings.keys():attrs.pop(k)attrs["__mappings__"] = mappingsattrs["__table__"] = name # 添加表名,假设表名与类名一致return type.__new__(cls, name, bases, attrs)# 编写Model基类继承自dict中,这样可以使用一些dict的方法
class Model(dict, metaclass=ModelMetaclass):def __init__(self,  **kw):# 调用父类,即dict的初始化方法super(Model, self).__init__(**kw)# 让获取key的值不仅仅可以d[k],也可以d.kdef __getattr__(self, key):try:return self[key]except KeyError:raise AttributeError(r"'Model' object has no attribute '%s'" % key)# 允许动态设置key的值,不仅仅可以d[k],也可以d.kdef __setattr__(self, key, value):self[key] = valuedef save(self):fields = []params = []args = []# 在所有映射中迭代for k, v in self.__mappings__.items():fields.append(v.name)params.append("?")args.append(getattr(self, k, None))sql = "insert into %s (%s) values (%s)" % (self.__table__, ",".join(fields), ",".join(params))print("SQL: %s" % sql)print("ARGS: %s" % str(args))# 这样一个简单的ORM就写完了# 下面实际操作一下,先定义个User类来对应数据库的表User
class User(Model):# 定义类的属性到列的映射id = IntegerField("id")name = StringField("username")email = StringField("email")password = StringField("password")# 创建一个实例
u = User(id=12345, name="ReedSun", email="sunhongzhao@foxmail.com", password="nicaicai")
u.save()

参考资料

  1. 使用元类-廖雪峰的官方网站
  2. 深刻理解python中的元类

python中的元类Metaclass相关推荐

  1. [转]深刻理解Python中的元类(metaclass)

    类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Pytho ...

  2. Python中的元类(metaclass)

    类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Pytho ...

  3. 深刻理解Python中的元类(metaclass)以及元类实现单例模式

    在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例模式的那一节有些疑惑.因此花了几天时间研究下元类这个概念.通过学习元类,我对pyt ...

  4. 深入理解Python中的元类(metaclass)

    注:本文原稿来自stackoverflow,原文链接,目前已收获5k高赞. 一.类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇 ...

  5. python中的类怎样理解_深入理解Python中的元类(metaclass)

    如何理解python当中的元类 把这个提到外面 class __metaclass__(type): " simple custom metaclass to block adding ne ...

  6. 理解Python中的元类(metaclass)

    类也是对象 在理解元类之前,你需要先掌握Python中的类.Python中类的概念借鉴于Smalltalk,这显得有些奇特.在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Pytho ...

  7. 深刻理解Python中的元类(metaclass)

    **注:**这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍 ...

  8. python元类_python中的元类 metaclass

    python中的元类 metaclass 在python中,类(class)本身也是一个实例对象, 它的类型则是元类, 如果没有指明, 则自定义类的类型是type. 换言之, 我们所定义的普通类都是t ...

  9. Python中的元类及元类实现的单例模式

    https://www.cnblogs.com/tkqasn/p/6524879.html 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元 ...

最新文章

  1. JBoss 系列八十: jBPM 6 中使用 jbpm-console 创建执行 BPM 流程 - I
  2. C语言函数指针(结构体函数指针)
  3. 《大道至简》的幕后故事
  4. 如何让Filddler抓包时忽略某些主机名
  5. linux下dds软件,【数据库】Linux 单实例环境下实现Oracle数据库和DDS软件的开机自动重启...
  6. 人工生命 2.0.1 版发布,给青蛙找个工作:走跷跷板
  7. 红帽初级认证RHCSA考试环境——供实验练习
  8. cad线性标注命令_CAD中线性标注的快捷命令是什么
  9. hive建表语句 增加字段、分区基础操作
  10. java如何统计txt的字数_Java HashSet对txt文本内容去重(统计小说用过的字或字数)...
  11. oracle 12c的PDB数据库未打开
  12. 【学习笔记】汇编语言入门
  13. 面试阿里数据研发岗,无缘三面,谈谈面经
  14. 基于TensorRT和onnxruntime下pytorch的Bert模型加速对比实践
  15. 随興8作者雨落下無痕
  16. R语言与概率统计(三) 多元统计分析(中)
  17. rust新版组队指令_腐蚀rust新版服务器指令大全 腐蚀指令一览
  18. 用迭代法求 a 的平方根。求平方根的迭代公式为····
  19. nmon结果分析工具_使用nmon analyzer 分析指标
  20. Chino with Train to the Rabbit Town

热门文章

  1. 逻辑回归(logistic regression)与正规化方程
  2. java crc8_java实现 CRC8 校验 多项式 x8+x2+x+1
  3. Fundebug录屏插件更新至0.6.0
  4. Centos安装mysql rpm包
  5. 腾讯也发文执行“965”工作了,7点后加班需申请?
  6. 如何建设一套高标准的智慧监所电教系统
  7. 对象在Hibernate中的三种状态
  8. 喵的Unity游戏开发之路 - 生成区:各色场景
  9. pbl和sbl_acca sbr和sbl通过率哪个更高?
  10. php字符串用什么括起来,PHP的字符串型数据,可以用以下哪些符号括起来()。