python3 metaclass--创建类的过程分析

xuewen1696 2018-06-27 15:54:20 2251 收藏 3

展开

之前学python的时候就看见过metaclass的文章,没看懂,那篇博客后面说到,metaclass是python的黑魔法99% 不会用到。于是果断放弃。

不过最近看flask-WTForm组建的源码,一开始就是metaclass。没办法,硬着头皮重新看metaclass。基本了解,现在总结如下:

一、metaclass干嘛的?

metaclass是指定类由谁创建。能够定制类的创建过程

指定类由谁创建的???开什么玩笑,类不是由'我'创建的吗????

python中一切皆对象,类也是对象,类是由type类创建。

我们写下如下代码时:

class Foo(object):

pass

实际上,解释器将其解释为:

Foo = type('Foo', (object,), {})

type()的三个参数:'Foo':类名; (object, ): 类的继承关系,用元组表示; {}: 类的字段,方法。

以上是类的默认创建方法。由type创建。python也给我们提供了自定义类的创建的方法,即metaclass。type也是类,它可以创建类,因此我们叫它元类,不要过分纠结这是什么鬼,知道type类可以创建类就行。

自定义类的创建过程,那就得写一个像type一样可以创建类的类,那简单,继承就可以办到。

方式一:

class MyType(type):

def __new__(cls, *args, **kwargs):

print('MyType __new__')

return super().__new__(cls, *args, **kwargs)

def __init__(cls, *args, **kwargs):

print('MyTpye __init__')

super().__init__(*args, **kwargs)

def __call__(cls, *args, **kwargs):

print('MyTpye __call__')

super().__call__(cls, *args, **kwargs)

class Foo(metaclass=MyType):

pass

这样,解释器解释到class Foo(...)的时候,就会转换为:

Foo = MyType('Foo', (object,), {})

方式二:

class MyType(type):

def __new__(cls, *args, **kwargs):

print('MyType __new__')

return super().__new__(cls, *args, **kwargs)

def __init__(cls, *args, **kwargs):

print('MyTpye __init__')

super().__init__(*args, **kwargs)

def __call__(cls, *args, **kwargs):

print('MyTpye __call__')

super().__call__(cls, *args, **kwargs)

def with_meta(meta, Base):

return meta('Foo', (Base, ), {})

class Foo(with_meta(MyType, object)):

pass

这样解释的时候,与方式一的一样。

二、创建类与类实例化时执行过程是怎样的?

解释器解释到class的定义语句时,会先在class中寻找是否指定自定义的'MyType', 没有再往父类找是否指定,没有再在本模块中找,是否本模块指定了统一的'MyType', 若均没有,则用默认的type创建。

解释到class Foo(...)时,会调用'MyType'的__new__, __init__方法。生成类。

解释到f = Foo() ,类的实例化时,会调用'MyType'的__call__方法,而'type'的__call__方法又会去调用Foo的__new__, __init__实例化类对象。

下面用一个实际的例子来说明元类的使用方法

三、ORM的元类实例:

#ORM:object relational mapping 对象-关系映射

#把关系数据库的一行映射为一个对象,也就是一个类对应一个表

#ORM框架所有的类只能动态定义

# 定义Field(定义域:元类遇到Field的方法或属性时即进行修改)

class Field(object):

def __init__(self, name, column_type): # column==>列类型

self.name = name

self.column_type = column_type

# 当用print打印输出的时候,python会调用他的str方法

# 在这里是输出

# 在ModelMetaclass中会用到

def __str__(self):

return "" % (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__"] = mappings

attrs["__table__"] = name # 添加表名,假设表名与类名一致

return type.__new__(cls, name, bases, attrs)

# 编写Model基类继承自dict中,这样可以使用一些的方法

class Model(dict, metaclass=ModelMetaclass):

def __init__(self, **kw):

# 调用父类,即dict的初始化方法

super(Model, self).__init__(**kw)

# 让获取key的值不仅仅可以d[k],也可以d.k

def __getattr__(self, key):

try:

return self[key]

except KeyError:

raise AttributeError(r"'Model' object has no attribute '%s'" % key)

# 允许动态设置key的值,不仅仅可以d[k],也可以d.k

def __setattr__(self, key, value):

self[key] = value

def 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类来对应数据库的表Userclass 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. 定义属性

class Field(object):

pass

# 进一步定义各种类型的Field

class StringField(Field):

pass

class IntegerField(Field):

pass

2.  操作属性:

# 编写ModelMetaclass

class ModelMetaclass(type):

def __new__(cls, name, bases, attrs):

pass

# 编写Model基类继承自dict中,这样可以使用一些的方法

class Model(dict, metaclass=ModelMetaclass):

def __init__(self, **kw):

# 调用父类,即dict的初始化方法

super(Model, self).__init__(**kw)

# 让获取key的值不仅仅可以d[k],也可以d.k

def __getattr__(self, key):

pass

# 允许动态设置key的值,不仅仅可以d[k],也可以d.k

def __setattr__(self, key, value):

self[key] = value

def save(self):

pass

# 下面实际操作一下,先定义个User类来对应数据库的表User

class User(Model):

# 定义类的属性到列的映射

id = IntegerField("id")

name = StringField("username")

email = StringField("email")

password = StringField("password")

3. 统管属性:

u = User(id=12345, name="ReedSun", email="sunhongzhao@foxmail.com", password="nicaicai")

u.save()

代码执行流程:

解释器执行到 class Model 时,知道其指定了由 ModelMetaclass创建。因此,解释(不是执行)完类内定义的方法后,跳进 ModelMetaclass 的 __new__(cls, name, base, attrs), ModelMetaclass 没有__init__,执行type的__init__。(由于是生成Model, __new__(cls, name, base, attrs)中的clc为Model)  至此Model类正式创建完毕。

解释器执行到class User 时,与上面一样, 解释完类内定义的 id, name, email , password 字段后(这些字段均为...Field对象),跳进ModelMetaclass 的 __new__(cls, name, base, attrs),此时 cls 为 User, name 为 'User',  base 为 Model, attrs 为类似{'id': IntegerField("id"), 'name':StringField("username"),.........}的字典。

建议最好自己设置断点,调式执行看看,就会明白执行流程是怎么样的。

四、定制类生成的好处:

当然是非常多的,最容易理解的,单例模式便可以用 metaclass来实现。其他的好处,如ORM,我还说不上来,等弄清楚再补充。

参考与推荐博文:

https://blog.csdn.net/weixin_35955795/article/details/52985170

http://blog.jobbole.com/21351/

https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014319106919344c4ef8b1e04c48778bb45796e0335839000

https://www.cnblogs.com/xybaby/p/7927407.html

————————————————

版权声明:本文为CSDN博主「xuewen1696」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/m0_37519490/article/details/80825934

python3创建类_python3 metaclass--创建类的过程分析相关推荐

  1. python3怎么创建文件_Python3.5 创建文件的简单实例

    实例如下所示: #coding=utf-8 ''' Created on 2012-5-29 @author: xiaochou ''' import os import time def nsfil ...

  2. python3怎么创建文件_python3怎么创建新文件

    python3创建新文件的方法:可以利用os.open()函数来创建新文件.函数语法为:[os.open(file, flags[, mode]);],如果flags参数为os.O_CREAT,表示创 ...

  3. python 抽象基类_python3 抽象基类

    1,抽象基类(Abstract base class或者 ABCs)用于 定义一组必须被类的"鸭子类型"实例实现的方法与属性,可以继承抽象基类本身的类作为类的实例,但是必须提供所有 ...

  4. 使用Python元类(metaclass)创建Python类

    Python类的定义再简单不过了.不过你有想过动态定义一个Python类吗?What?动态定义,是动态解析Python代码吗?不,这仍然是静态定义.我是说,干预类的创建过程,在类的创建过程中,对类中的 ...

  5. python类与函数编程_Python类三种方法,函数传参,类与实例变量(一)详解

    1 Python的函数传递: 首先所有的变量都可以理解为内存中一个对象的'引用' a = 1 def func(a): a = 2 func(a) print(a) # 1 a = 1 def fun ...

  6. python 元类 type_Python 使用元类type创建类对象常见应用详解

    本文实例讲述了Python 使用元类type创建类对象.分享给大家供大家参考,具体如下: type("123") 可以查看变量的类型;同时 type("类名", ...

  7. Django站点管理、视图和URL(管理界面本地化、创建管理员、注册模型类、发布内容到数据库、定义视图、配置URLconf)

    1.Django站点管理 站点: 分为内容发布和公共访问两部分 内容发布的部分由网站的管理员负责查看.添加.修改.删除数据 Django能够根据定义的模型类自动地生成管理模块 使用Django的管理模 ...

  8. [WinAPI] API 4 [注册][创建][消息][第一个框架类窗口]

    首先注册了窗口类,然后创建了一个窗口,创建窗口时指定的窗口的属性和窗口消息的处理函数.函数消息的处理函数大多调用系统默认函数来处理. 1 #include<windows.h> 2 3 / ...

  9. java class对象创建时机_Java面向对象编程-类的声明周期

    第十章 类的生命周期 10.1 Java虚拟机及程序的生命周期 当通过java命令运行一个Java程序时,就启动了一个Java虚拟机进程.Java虚拟机进程从启动到终止的过程,称为Java虚拟机的生命 ...

最新文章

  1. WPF 回车转Tab实现跳转
  2. 机器学习---实战---K-近邻算法--笔记
  3. LightOJ - 1409 Rent a Car(最小费用最大流)
  4. mysql模糊查询LIKE、REGEXP(正则)的详解(在可视化工具navicat下)
  5. 12星座的出生年月日性格_星座六点半/今日12月04日:双鱼、巨蟹、天蝎座运势...
  6. 通过Java得到的时间与操作系统时间不一致,如何修改Java虚拟机时间?
  7. Linux网络处理“零拷贝”技术mmap()内核进程间通信设计8086分页管理——摆在一起来谈谈...
  8. Ecology 查询某人所有流程待办事项
  9. ICLR'17 | 在特征空间增强数据集
  10. java if ( 常量==变量)_Java常量、变量和运算符
  11. [转载] windows安装mysql及使用navicat连接
  12. POJ 2686 Traveling by Stagecoach
  13. android逆向工程反编译指南(详细教程)
  14. 计算机硬盘的常用分区工具,磁盘分区工具,详细教您硬盘分区工具diskgenius怎么使用...
  15. 数据分析-数据清洗与整理
  16. 画图计算机应用基础,计算机应用基础教案:Windows XP附件——写字板、画图.doc...
  17. 管理学定律--墨菲定律
  18. VI设计中系统视觉基本要素优漫动游
  19. V4L2驱动详解 API翻译
  20. 3D建模软件大总结,你都知道哪些?

热门文章

  1. dlsym 如何查看一个so里面的_如何查看并且使用android系统本身包含的so库?
  2. 小熊派折叠开发板Docker编译烧录安装HAP
  3. android 7.0电视,将智能电视升级到Android 7.0有什么经验?为索尼用户祝福
  4. vimpython配色_超漂亮 vim 配置:space-vim
  5. selenium得到一个动态页面
  6. w ndows太卡,用Windows 10太卡?教你快速干掉Windows Defender
  7. jq 鼠标指向隐藏显示区域
  8. spring MVC 项目 WEB-INF下的jsp不能加载css文件
  9. bat(续六)-windows批处理set命令
  10. openssl生成私钥公钥的步骤