`

浅析python的metaclass

 
阅读更多

分享下自己对python的metaclass的知识。

 

一 你可以从这里获取什么?

1. 也许你在阅读别人的代码的时候碰到过metaclass,那你可以参考这里的介绍。

2. 或许你需要设计一些底层的库,也许metaclass能帮你简化你的设计(也有可能复杂化:)

3. 也许你在了解metaclass的相关知识之后,你对python的类的一些机制会更了解。

4. more......

 

二 metaclass的作用是什么?(感性认识)

 metaclass能有什么用处,先来个感性的认识:

1. 你可以自由的、动态的修改/增加/删除 类的或者实例中的方法或者属性

2. 批量的对某些方法使用decorator,而不需要每次都在方法的上面加入@decorator_func

3. 当引入第三方库的时候,如果该库某些类需要patch的时候可以用metaclass

4. 可以用于序列化(参见yaml这个库的实现,我没怎么仔细看)

5. 提供接口注册,接口格式检查等

6. 自动委托(auto delegate)

7. more...

 

三 metaclass的相关知识

1. what is metaclass?

1.1 在wiki上面,metaclass是这样定义的:In object-oriented programming, 

a metaclass is a class whose instances are classes. 

Just as an ordinary class defines the behavior of certain objects, 

a metaclass defines the behavior of certain classes and their instances.

 

也就是说metaclass的实例化结果是类,而class实例化的结果是instance。我是这么理解的:

metaclass是类似创建类的模板,所有的类都是通过他来create的(调用__new__),这使得你可以自由的控制

创建类的那个过程,实现你所需要的功能。

 

1.2 metaclass基础

* 一般情况下, 如果你要用类来实现metaclass的话,该类需要继承于type,而且通常会重写type的__new__方法来控制创建过程。

当然你也可以用函数的方式(下文会讲)

* 在metaclass里面定义的方法会成为类的方法,可以直接通过类名来调用

 

2. 如何使用metaclass

2.1 用类的形式

2.1.1 类继承于type, 例如: class Meta(type):pass

2.1.2 将需要使用metaclass来构建class的类的__metaclass__属性(不需要显示声明,直接有的了)赋值为Meta(继承于type的类)

 

2.2 用函数的形式

2.2.1 构建一个函数,例如叫metaclass_new, 需要3个参数:name, bases, attrs,

name: 类的名字

bases: 基类,通常是tuple类型

attrs: dict类型,就是类的属性或者函数

2.2.2 将需要使用metaclass来构建class的类的__metaclass__属性(不需要显示声明,直接有的了)赋值为函数metaclas_new

 

3 metaclass 原理

3.1 basic

metaclass的原理其实是这样的:当定义好类之后,创建类的时候其实是调用了type的__new__方法为这个类分配内存空间,创建

好了之后再调用type的__init__方法初始化(做一些赋值等)。所以metaclass的所有magic其实就在于这个__new__方法里面了。

说说这个方法:__new__(cls, name, bases, attrs)

cls: 将要创建的类,类似与self,但是self指向的是instance,而这里cls指向的是class

name: 类的名字,也就是我们通常用类名.__name__获取的。

bases: 基类

attrs: 属性的dict。dict的内容可以是变量(类属性),也可以是函数(类方法)。

 

所以在创建类的过程,我们可以在这个函数里面修改name,bases,attrs的值来自由的达到我们的功能。这里常用的配合方法是

getattr和setattr(just an advice)

 

3.2 查找顺序

再说说关于__metaclass__这个属性。这个属性的说明是这样的:

This variable can be any callable accepting arguments for name, bases, and dict. Upon class creation, the callable is used instead of the built-in type(). New in version 2.2.(所以有了上面介绍的分别用类或者函数的方法)

 

The appropriate metaclass is determined by the following precedence rules: 

If dict['__metaclass__'] exists, it is used. 

Otherwise, if there is at least one base class, its metaclass is used (this looks for a __class__ attribute first and if not found, uses its type). 

Otherwise, if a global variable named __metaclass__ exists, it is used. 

Otherwise, the old-style, classic metaclass (types.ClassType) is used. 

 

这个查找顺序也比较好懂,而且利用这个顺序的话,如果是old-style类的话,可以在某个需要的模块里面指定全局变量

__metaclass__ = type就能把所有的old-style 变成 new-style的类了。(这是其中一种trick)

 

四 例子

针对第二点说的metaclass的作用,顺序来给些例子:

1. 你可以自由的、动态的修改/增加/删除 类的或者实例中的方法或者属性

 

Python代码  收藏代码
  1. #!/usr/bin/python  
  2. #coding :utf-8  
  3.   
  4.   
  5. def ma(cls):  
  6.     print 'method a'  
  7.   
  8. def mb(cls):  
  9.     print 'method b'  
  10.   
  11. method_dict = {  
  12.     'ma': ma,  
  13.     'mb': mb,  
  14. }  
  15.   
  16. class DynamicMethod(type):  
  17.     def __new__(cls, name, bases, dct):  
  18.         if name[:3] == 'Abc':  
  19.             dct.update(method_dict)  
  20.         return type.__new__(cls, name, bases, dct)  
  21.   
  22.     def __init__(cls, name, bases, dct):  
  23.         super(DynamicMethod, cls).__init__(name, bases, dct)  
  24.   
  25.   
  26. class AbcTest(object):  
  27.     __metaclass__ = DynamicMethod  
  28.     def mc(self, x):  
  29.         print x * 3  
  30.   
  31. class NotAbc(object):  
  32.     __metaclass__ = DynamicMethod  
  33.     def md(self, x):  
  34.         print x * 3  
  35.   
  36. def main():  
  37.     a = AbcTest()  
  38.     a.mc(3)  
  39.     a.ma()  
  40.     print dir(a)  
  41.   
  42.     b = NotAbc()  
  43.     print dir(b)  
  44.   
  45. if __name__ == '__main__':  
  46.     main()  

 

通过DynamicMethod这个metaclass的原型,我们可以在那些指定了__metaclass__属性位DynamicMethod的类里面,

根据类名字,如果是以'Abc'开头的就给它加上ma和mb的方法(这里的条件只是一种简单的例子假设了,实际应用上

可能更复杂),如果不是'Abc'开头的类就不加. 这样就可以打到动态添加方法的效果了。其实,你也可以将需要动态

添加或者修改的方法改到__new__里面,因为python是支持在方法里面再定义方法的. 通过这个例子,其实可以看到

只要我们能操作__new__方法里面的其中一个参数attrs,就可以动态添加东西了。

 

 

2. 批量的对某些方法使用decorator,而不需要每次都在方法的上面加入@decorator_func

这个其实有应用场景的,就是比如我们cgi程序里面,很多需要验证登录或者是否有权限的,只有验证通过之后才

可以操作。那么如果你有很多个操作都需要这样做,我们一般情况下可以手动在每个方法的前头用@login_required

类似这样的方式。那如果学习了metaclass的使用的话,这次你也可以这样做:

 

Python代码  收藏代码
  1. #!/usr/bin/python  
  2. #coding :utf-8  
  3. from types import FunctionType  
  4.   
  5. def login_required(func):  
  6.     print 'login check logic here'  
  7.     return func  
  8.   
  9.   
  10. class LoginDecorator(type):  
  11.     def __new__(cls, name, bases, dct):  
  12.         for name, value in dct.iteritems():  
  13.             if name not in ('__metaclass__''__init__''__module__'and\  
  14.                 type(value) == FunctionType:  
  15.                 value = login_required(value)  
  16.   
  17.             dct[name] = value  
  18.         return type.__new__(cls, name, bases, dct)  
  19.   
  20.   
  21. class Operation(object):  
  22.     __metaclass__ = LoginDecorator  
  23.   
  24.     def delete(self, x):  
  25.         print 'deleted %s' % str(x)  
  26.   
  27.   
  28. def main():  
  29.     op = Operation()  
  30.     op.delete('test')  
  31.   
  32. if __name__ == '__main__':  
  33.     main()  

 

这样子你就可以不用在delete函数上面写@login_required, 也能达到decorator的效果了。不过可读性就差点了。

 

 

3. 当引入第三方库的时候,如果该库某些类需要patch的时候可以用metaclass

 

Python代码  收藏代码
  1. #!/usr/bin/python  
  2. #coding :utf-8  
  3.   
  4. def monkey_patch(name, bases, dct):  
  5.     assert len(bases) == 1  
  6.     base = bases[0]  
  7.     for name, value in dct.iteritems():  
  8.         if name not in ('__module__''__metaclass__'):  
  9.             setattr(base, name, value)  
  10.     return base  
  11.   
  12. class A(object):  
  13.     def a(self):  
  14.         print 'i am A object'  
  15.   
  16.   
  17. class PatchA(A):  
  18.     __metaclass__ = monkey_patch  
  19.   
  20.     def patcha_method(self):  
  21.         print 'this is a method patched for class A'  
  22.   
  23. def main():  
  24.     pa = PatchA()  
  25.     pa.patcha_method()  
  26.     pa.a()  
  27.     print dir(pa)  
  28.     print dir(PatchA)  
  29.   
  30. if __name__ == '__main__':  
  31.     main()  

 

 

5. 提供接口注册,接口格式检查等, 这个功能可以参考这篇文章:

http://mikeconley.ca/blog/2010/05/04/python-metaclasses-in-review-board/

 

 

6. 自动委托(auto delegate)

以下是网上的例子:

http://marlonyao.iteye.com/blog/762156

 

五 总结

1. metaclass的使用原则:

If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). --Tim Peters

也就是说如果你不知道能用metaclass来干什么的话,你尽量不要用,因为通常metaclass的代码会增加代码的复杂度,

降低代码的可读性。所以你必需权衡metaclass的利弊。

2. metaclass的优势在于它的动态性和可描述性(比如上面例子中的self.delegate.__getitem__(i)这样的代码,它

可以用另外的函数代码生成,而无需每次手动编写), 它能把类的动态性扩展到极致。

 

六 补充

以下是同事们的很好的补充:

张同学:

1.metaclass属于元编程(metaprogramming)的范畴,所谓元编程就是让程序来写

(generate/modify)程序,这通常依赖于语言及其运行时系统的动态特性(其实像C

这样的语言也可以进行元编程)。正如楼主所说,元编程的一个用途就是“可以用另

外的函数代码生成,而无需每次手动编写“,在python中我们可以做得更多。

 

2.对于python而言,metaclass使程序员可以干涉class的创建过程,并可以在任何

时候修改这样的class(包括修改metaclass),由于class的意义是为instance集合

持有“方法”,所以修改了一个class就等于修改了所有这些instance的行为,这是

很好的service。

 

3.注意metaclass的__new__和__init__的区别。

class DynamicMethod(type):

    def __new__(cls, name, bases, dct):  # cls=DynamicMethod

    def __init__(cls, name, bases, dct): # cls=你创建的class对象

这意味着在__new__中我们通常只是修改dct,但是在__init__中,我们可以直接修

改创建好的类,所以我认为这两个接口的主要区别有2点:1)调用时机不同(用处请

发散思维);2)__init__比__new__更有用,我在实际项目中一般都是用__init__的。

 

4.在python中我们为什么要修改class?那当然是为了改变它的行为,或者为了创

建出独一无二的类。实际中常常需要为class动态添加方法。比如一个数据库表A有

字段name, address等,表B有name, phone等,你希望A的模型类有

find_by_address、find_by_name_and_address等方法,希望B的模型类有

find_by_name、find_by_phone等方法,但是又不想手写这些方法(其实不可能手

写,因为这种组合太多了),这时你可以在A、B共同的metaclass中定义一个自动添

加方法的子程序,当然你也可以重写__getattr__之类的接口来hook所有

find_by_XXX函数调用,这就是时间换空间了,想象你要执行find_by_XXX一百万

次。也可以比较一下在c++/java中如何应对这种需求。

 

5.python的成功之处在于它无处不在的namespace(就是那个__dict__,其意义可以

参考SICP第一章的environment模型,对计算理论感兴趣的也可以用lambda演算来

解释),而且函数又是first class对象,又强化了interface的使用。我们知道了

metaclass->class->instance的关系,又知道了对象的方法是放在类里的(请详细

考察python查找一个方法的流程),那么用python实现各种设计模式就比较简单了。

 

6.metaclass不会使程序变晦涩,前提是了解了metaclass的固有存在,许多教程的

问题就在于它没有告诉读者metaclass的存在,而是借助某些其他语言(比如c++)的

类模型来讲解python。在ruby的类型系统中metaclass是无限的,metaclass也有自

己的metaclass(你可以称之为metametaclass、metametametaclass等等),ruby

善于实现DSL和语法分析器也部分得益于此。

 

岳同学:

不能说__init__比__new__更有用吧。我觉得要看场合。毕竟__new__能做到比

__init__更多的事情。比如有时候想改生成的类型名字,或者改类型的父类。:)

不过的确大多数场合用__init__就够用了。+1

在__init__中控制类生成的过程有一点要注意:在__init__()的最后一个参

数(attrs)中,对于类中定义的函数类型的属性,比如:

def abc(self):

     pass

仍然具有以下的key->value形式:

"abc":<function object>

但是在生成的类中,"abc"对应的属性已经从一个function变成了一个unbind

method:

self.abc --> unbind method

不过实际使用中影响不大。

 

 

七 其他

1. 哪些项目有用metaclass:

据我所知就是django中的数据库部分的很多都使用metaclass来实现可描述性的

还有google app engine的代码里面也有使用

yaml中的序列化部分代码也有使用

more...

 

2. 参考资料:

* Metaclass Programming In Python [http://gnosis.cx/publish/programming/metaclass_1.html]

* Python中用MetaClass实现委托、不可变集合 [http://marlonyao.iteye.com/blog/762156]

* Metaclass [http://en.wikipedia.org/wiki/Metaclasses#Python_example]

 

3. 以上都是个人的观点和总结而已,欢迎拍砖。

分享到:
评论

相关推荐

    浅析Python+OpenCV使用摄像头追踪人脸面部血液变化实现脉搏评估

    本文介绍了一种使用Python语言结合OpenCV库来追踪人脸面部血液变化以评估脉搏的技术。此技术通过摄像头捕捉面部图像并分析血液流动引起的微小颜色变化,从而实时评估脉搏。 Python是一种广泛使用的高级编程语言,因...

    浅析Python爬虫获取数据实现调查研究的应用.zip

    Python爬虫在获取数据实现调查研究中的应用广泛且深入,它是现代数据分析、市场研究和决策支持的重要工具。本文将详细探讨Python爬虫的基础知识、如何使用Python爬虫抓取网络数据,以及在实际调查研究中如何运用这些...

    浅析基于Python爬虫技术的特性及应用.zip

    浅析基于Python爬虫技术的特性及应用

    浅析Python垃圾回收机制.pdf

    "浅析Python垃圾回收机制" Python垃圾回收机制是指在Python程序执行过程中,动态申请内存空间,并在不再需要使用这些内存空间时释放它们,以避免内存泄漏。Python中的垃圾回收机制主要以引用计数为主,标记清除和分...

    浅析Python在油气勘探开发中的应用与发展-顾晓东.pdf

    浅析Python在油气勘探开发中的应用与发展 Python是一种广泛应用于油气勘探开发领域的编程语言,因为它具有易于学习、高可读性和庞大的第三方库等优点。在油气勘探开发领域,Python的应用主要体现在数据处理、地质...

    浅析Python多线程与多进程的使用

    Python是当今世界上最受欢迎的编程语言之一,特别是在处理数据科学、机器学习和Web开发等领域。然而,随着程序规模的扩大和复杂性的增加,如何有效利用计算机的多核资源变得至关重要。本篇文章将深入探讨Python中的...

    浅析Python语言在县级坡度分级图制作中的应用.pdf

    Python语言在地理信息系统(GIS)中的应用已经越来越广泛,特别是在制作县级坡度分级图这类需要大量空间数据处理的工作中。本文通过对河南省第二次土地调查中县级坡度分级图制作过程的分析,展示了Python语言如何...

    浅析Python生成器.pdf

    浅析 Python 生成器 在 Python 中,生成器(generator)是一种特殊的可迭代对象,它可以在循环中动态地计算和生成元素,而不需要事先创建一个完整的列表。这种机制可以大大节省内存空间,提高程序的效率。 什么是...

    浅析Python爬虫获取数据实现调查研究的应用.pdf

    Python作为一种高效、便捷的高级编程语言,在构建爬虫应用方面具有显著的优势。 本文将通过实例“阿尔茨海默病患者及护理人员在网络社区寻求解决方案和情感支持”的信息调查,来展示如何使用Python编程语言来编写...

    浅析python在地图处理中的运用.pdf

    本文主要探讨Python语言在地图处理领域的应用,旨在介绍一种有效的地形图处理方法,从而提升工作效率并简化处理过程。 首先,文章提到了地图处理在计算制图中的重要性。作者通过个人经验说明了地形图配准与切割工作...

    浅析python在地图处理中的运用.zip

    Python是一种强大的编程语言,尤其在数据处理和可视化领域,它有着广泛的应用。在地图处理方面,Python凭借其易用性、丰富的库支持以及与各种地理信息系统(GIS)的兼容性,已经成为许多地理分析师和数据科学家的...

    浅析Python语言编程在红绿灯中的应用.pdf

    Python语言作为一种高效、简洁的编程语言,在多个领域有着广泛的应用,其中就包括模拟交通信号灯的控制程序。在深入探讨Python语言编程在红绿灯控制中的应用之前,先来了解Python语言的一些基本特性。 Python是一种...

    浅析Python语言编程在红绿灯中的应用.zip

    Python语言在红绿灯控制系统中的应用是一个典型的软硬件结合实例,它展示了计算机编程在实际生活中的强大作用。本文将深入探讨Python如何被用于红绿灯的自动化管理和优化,旨在帮助读者理解Python在智能交通系统中的...

    浅析Python语言在县级坡度分级图制作中的应用.zip

    Python语言在县级坡度分级图制作中的应用是一个广泛且实用的话题,尤其对于地理信息系统(GIS)和数据分析领域。本文将深入探讨如何利用Python强大的库,如GDAL、NumPy、matplotlib以及geopandas等,来处理地理数据...

    深入浅析python继承问题

    class p1: def __init__(self,a,b): print("init in p1") self.a1=a self.b1=b self.f1() def f1(self): print("f1 in p1") class c1(p1): def __init__(self,a,b,c=2): print("init in c1") p1.__init__(self,a,b) ...

    浅析python,PyCharm,Anaconda三者之间的关系

    Python,PyCharm,Anaconda这三者在Python编程领域中各司其职,相互协作,为开发者提供了高效的工作环境。下面将详细阐述它们的关系及其各自的功能。 首先,Python是一种高级编程语言,以其简洁易读的语法而闻名,...

    浅析基于Python爬虫技术的特性及应用.pdf

    本文主要探讨了基于Python语言的爬虫技术,包括它的特性、应用以及与其他编程语言和框架的比较。 首先,文章提到了爬虫技术的重要性。以Google公司的发展为例,初创时期的Google仅有一个老服务器和一个Python网络...

    Python提取频域特征知识点浅析

    首先创建有一个Python文件,并导入库文件: from scipy.io import wavfile from python_speech_features import mfcc, logfbank import matplotlib.pylab as plt1、首先创建有一个Python文件,并导入库文件: ...

    浅析Python四种数据类型

    在Python编程语言中,数据类型是构成程序的基本元素之一,它定义了变量或表达式所能取的数据种类。Python支持多种数据类型,其中包括整数、浮点数、字符串、布尔型、列表、元组、字典、集合等。不过,根据给定文件的...

    深入浅析python 协程与go协程的区别

    【深入浅析Python协程与Go协程的区别】 在计算机科学中,进程、线程和协程是并发执行任务的三种基本方式。进程是程序在操作系统中的实例,包含内存空间和资源;线程是进程中的执行单元,是CPU调度的基本单位,同一...

Global site tag (gtag.js) - Google Analytics