python中metaclass原理与用法详解


Posted in Python onJune 25, 2019

本文实例讲述了python中metaclass原理与用法。分享给大家供大家参考,具体如下:

什么是 metaclass.

metaclass (元类)就是用来创建类的类。在前面一篇文章《python动态创建类》里我们提到过,可以用如下的一个观点来理解什么是metaclass:

MyClass = MetaClass()
MyObject = MyClass()

metaclass是python 里面的编程魔法

同时在前面一篇《python动态创建类》文章里描述动态创建class 的时候介绍了type,他允许你用如下的方法创建一个类:

MyClass = type('MyClass', (), {})

其根本原因就在于 type 就是一个 metaclass, python利用type在后面创建各种各样的类。搞不明白的是,为什么是 "type" 而不是 "Type",可能考虑到 str 是用来创建字符串的,int 是用来 创建整形对象,所以type 用来创建 class object的,都用小写好了。

在python中的任何东西都是对象。包括int,str,function,class等。他们都是从一个class  创建的,我们可以通过查看 __class__ 属性来检查.

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

检查__class__属性

>>> a.__class__.__class__
<type 'type'>
>>> age.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

发现什么了,结果都是 "type", 其实  type 就是python内置的一个metaclass.当然,你可以创建自己的metaclass. 这里有一个很重要的属性:

__metaclass__ 属性

当你在写一个class的时候,你可以加入__metaclass__属性.

class Foo(object):
 __metaclass__ = something...
 [...]

如果你这么做了,那么python 将调用 metaclass 去创建 Foo class, 感觉是不是让你有点困惑呢。

python 将在你的class定义中查找__metaclass__,如果找到,就会用这个metaclass去创建Foo class,如果没有找到,就会用 type 去创建class.如果上篇文章提到的一样.所以,当你

class Foo(Bar):
 pass

pyton 将会如下去解析:是否有__metaclass__ 在Foo 里面,如果是的,则用metaclass  去创建一个名字为 ”Foo" 的class object. 如果没有找到,则看其基类Bar里面是否有__metaclass__,如果基类没有,则看所在的module 层是否有__metaclass__,如果都没有的话,则调用 type 去创建这个类。

现在的问题是,__metaclass__ 里面到底能做什么?结论是:能创建一个class的东西。什么能创建一个class, 其实就是 type,或者type 的子类(subclass)。

自定义 metaclass

metaclass的主要目的就是在创建类的时候,做一些自动的改变。比如,打个不恰当的比方,我们打算将一个module里所有类的属性都变成大写的。其中一种处理办法就是用 __metaclass__(申明在module上).

我们打算利用 metaclass 把所有的属性变成大写的。__metaclass__并不一定要求是一个class, 是一个可以调用的方法也是可以的。我们就从一个简单的例子看起

def upper_attr(future_class_name, future_class_parents, future_class_attr):
 """
  Return a class object, with the list of its attribute turned
  into uppercase. """
 # pick up any attribute that doesn't start with '__'
 attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
 # turn them into uppercase
 uppercase_attr = dict((name.upper(), value) for name, value in attrs)
 # let `type` do the class creation
 return type(future_class_name, future_class_parents, uppercase_attr)
__metaclass__ = upper_attr # this will affect all classes in the module
class Foo(): # global __metaclass__ won't work with "object" though
 # but we can define __metaclass__ here instead to affect only this class
 # and this will work with "object" childrend
 bar = 'bip'
print hasattr(Foo, 'bar')
# Out: False
print hasattr(Foo, 'BAR')
# Out: True
f = Foo()
print f.BAR
# Out: 'bip'

现在用一个类来处理

# remember that `type` is actually a class like `str` and `int`
# so you can inherit from it
class UpperAttrMetaclass(type):
  # __new__ is the method called before __init__
  # it's the method that creates the object and returns it
  # while __init__ just initializes the object passed as parameter
  # you rarely use __new__, except when you want to control how the object
  # is created.
  # here the created object is the class, and we want to customize it
  # so we override __new__
  # you can do some stuff in __init__ too if you wish
  # some advanced use involves overriding __call__ as well, but we won't
  # see this
  def __new__(upperattr_metaclass, future_class_name,
        future_class_parents, future_class_attr):
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return type(future_class_name, future_class_parents, uppercase_attr)

显然这不是很oop的做法,直接调用了type方法,而不是调用父类的__new__方法,下面这么做:

class UpperAttrMetaclass(type):
  def __new__(upperattr_metaclass, future_class_name,
        future_class_parents, future_class_attr):
    attrs = ((name, value) for name, value in future_class_attr.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    # reuse the type.__new__ method
    # this is basic OOP, nothing magic in there
    return type.__new__(upperattr_metaclass, future_class_name,
              future_class_parents, uppercase_attr)

你可能注意到 upperattr_metaclass ,这其实就相于self,普通类方法里的self.一个更通用的方法如下:

class UpperAttrMetaclass(type):
  def __new__(cls, name, bases, dct):
    attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
    uppercase_attr = dict((name.upper(), value) for name, value in attrs)
    return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

通过上面的例子可以了解metaclass了,也了解了在__init__方法,__new__方法里去做一个hook.当然还可以在__call__里面做文章,但更多的人喜欢在__init__里面修改 。

希望本文所述对大家Python程序设计有所帮助。

Python 相关文章推荐
Python中无限元素列表的实现方法
Aug 18 Python
Python中join和split用法实例
Apr 14 Python
python根据url地址下载小文件的实例
Dec 18 Python
Python爬虫实现获取动态gif格式搞笑图片的方法示例
Dec 24 Python
用xpath获取指定标签下的所有text的实例
Jan 02 Python
实例讲解Python中浮点型的基本内容
Feb 11 Python
python pip源配置,pip配置文件存放位置的方法
Jul 12 Python
Django 查询数据库并返回页面的例子
Aug 12 Python
python实现截取屏幕保存文件,删除N天前截图的例子
Aug 27 Python
浅谈pytorch中的BN层的注意事项
Jun 23 Python
详解Python爬虫爬取博客园问题列表所有的问题
Jan 18 Python
Python 的演示平台支持 WSGI 接口的应用
Apr 20 Python
python实现接口并发测试脚本
Jun 25 #Python
Python实现EXCEL表格的排序功能示例
Jun 25 #Python
python实现动态创建类的方法分析
Jun 25 #Python
python pandas写入excel文件的方法示例
Jun 25 #Python
python多线程http压力测试脚本
Jun 25 #Python
Pyqt5 基本界面组件之inputDialog的使用
Jun 25 #Python
对PyQt5的输入对话框使用(QInputDialog)详解
Jun 25 #Python
You might like
JAVA/JSP学习系列之六
2006/10/09 PHP
PHP CURL模拟登录新浪微博抓取页面内容 基于EaglePHP框架开发
2012/01/16 PHP
PHP基于Closure类创建匿名函数的方法详解
2017/08/17 PHP
PHP调用其他文件中的类
2018/04/02 PHP
TP5框架实现签到功能的方法分析
2020/04/05 PHP
Javascript SHA-1:Secure Hash Algorithm
2006/12/20 Javascript
Javascript实例教程(19) 使用HoTMetal(2)
2006/12/23 Javascript
boxy基于jquery的弹出层对话框插件扩展应用 弹出层选择器
2010/11/21 Javascript
一个JQuery写的点击上下滚动的小例子
2011/08/27 Javascript
手机平板等移动端适配跳转URL的js代码
2014/01/25 Javascript
jquery选择器之内容过滤选择器详解
2014/01/27 Javascript
使用GruntJS构建Web程序之构建篇
2014/06/04 Javascript
JQuery 在线引用及测试引用是否成功
2014/06/24 Javascript
js模仿php中strtotime()与date()函数实现方法
2015/08/11 Javascript
JS自定义混合Mixin函数示例
2016/11/26 Javascript
浅谈Vue的基本应用
2016/12/27 Javascript
js实现固定宽高滑动轮播图效果
2017/01/13 Javascript
微信小程序实战之登录页面制作(5)
2020/03/30 Javascript
JS正则验证多个邮箱完整实例【邮箱用分号隔开】
2017/04/19 Javascript
NodeJS爬虫实例之糗事百科
2017/12/14 NodeJs
微信小程序系列之自定义顶部导航功能
2019/05/21 Javascript
mui js控制开关状态、修改switch开关的值方法
2019/09/03 Javascript
laravel实现中文和英语互相切换的例子
2019/09/30 Javascript
JavaScript this在函数中的指向及实例详解
2019/10/14 Javascript
django模型层(model)进行建表、查询与删除的基础教程
2017/11/21 Python
python的Crypto模块实现AES加密实例代码
2018/01/22 Python
python 实现读取一个excel多个sheet表并合并的方法
2019/02/12 Python
Python下opencv图像阈值处理的使用笔记
2019/08/04 Python
详解Python3迁移接口变化采坑记
2019/10/11 Python
python matplotlib模块基本图形绘制方法小结【直线,曲线,直方图,饼图等】
2020/04/26 Python
tensorflow之读取jpg图像长和宽实例
2020/06/18 Python
HTML5的结构和语义(1):前言
2008/10/17 HTML / CSS
在印度上传处方,在线订购药品:Medlife
2019/03/28 全球购物
加拿大留学自荐信
2014/01/28 职场文书
自强自立美德少年事迹材料
2014/08/16 职场文书
教学反思怎么写
2016/02/24 职场文书