(博客原创作品,转载请注明出处!)

  最近接触到了Python中的decorator,metaclass,abc Module,six.add_metaclass等内容,这里做一个简单的笔记。

  主要资源:

  1. PEP3119:Abstract Base Classes

  2. abc模块:abc Moduleabc—Abstract Base Classes

  3. metaclass: “Python中metaclass解释”、浅析python的metaclassPEP3115

  4. 相关:Python 3 初探,第 2 部分: 高级主题

  5. six.add_metaclass: six Module

  

  装饰器的引入纯粹是一个“语法糖”,即让代码看起来更加易懂。装饰器引入前Python中已经存在了“class method”, "static method"等包裹函数,不使用装饰器的结果是如果一个方法要被声明为class method,那么在他的“def”语句结束后需要立即使用"classmethod"将其注册成类方法。这样有一些弊端:当代码的读者开始读这个函数的时候,他一般看不到末尾的"classmethod"语句,所以可能直到看完整个函数的定义才知道这是一个类方法,也即是最初没有装饰器时在定义的结尾对方法进行装饰的设定比较反人类;另外采用 method = classmethod(method) 方式写出来的代码,设计Python的大神们觉得 method 竟然重复出现了两次太多了,写这两次 method 的时间已经够他们喝一壶的了,所以引入了更为简洁的decorator。

  装饰器以“@”标识,实质上是一层包裹函数,即函数的函数。写在函数定义( def 语句)的前面,表示 def 语句后定义的函数受到装饰器的装饰,也就是说这个新定义的函数刚刚出生,立刻在函数定义结束的下一行代码运行装饰器给她穿点衣服遮羞。

  metaclass是“类的类”,秉承Python“一切皆对象”的理念,Python中的类也是一类对象,metaclass的实例就是类(class),自己写metaclass时需要让其继承自type对象。关于metaclass的介绍,“主要资源”中相关的链接,不做赘述。

  ABC(抽象基类),主要定义了基本类和最基本的抽象方法,可以为子类定义共有的API,不需要具体实现。

  abc模块,Python 对于ABC的支持模块,定义了一个特殊的metaclass—— ABCMeta 还有一些装饰器—— @abstractmethod 和 @abstarctproperty 。

   abc.ABCMeta 是一个metaclass,用于在Python程序中创建抽象基类。

  抽象基类可以不实现具体的方法(当然也可以实现,只不过子类如果想调用抽象基类中定义的接口需要使用super())而是将其留给派生类实现。抽象基类可以被子类直接继承,也可以将其他的类”注册“(register)到其门下当虚拟子类,虚拟子类的好处是你实现的第三方子类不需要直接继承自基类但是仍然可以声称自己子类中的方法实现了基类规定的接口(issubclass(), issubinstance())!

  虚拟子类是通过调用metaclass是 abc.ABCMeta 的抽象基类的 register 方法注册到抽象基类门下的,可以实现抽象基类中的部分API接口,也可以根本不实现,但是issubclass(), issubinstance()进行判断时仍然返回真值。

  直接继承抽象基类的子类就没有这么灵活,在metaclass是 abc.ABCMeta的抽象基类中可以声明”抽象方法“和“抽象属性”,直接继承自抽象基类的子类虽然判断issubclass()时为真,但只有完全覆写(实现)了抽象基类中的“抽象”内容后,才能被实例化,而通过注册的虚拟子类则不受此影响。

  metaclass为 abc.ABCMeta 的抽象基类如果想要声明“抽象方法”,可以使用abc模块中的装饰器 @abstractmethod ,如果想声明“抽象属性”,可以使用abc模块中的 @abstractproperty 。

  最后,为什么要提six模块呢,six模块是Python为了兼容Python 2.x 和Python 3.x提供的一个模块,该模块中有一个针对类的装饰器 @six.add_metaclass(MetaClass) 可以为两个版本的Python类方便地添加metaclass。这样我们就可以同时利用Python中的abc模块和six模块在类的定义前添加 @six.add_metaclass(abc.ABCMeta) 来优雅地声明一个抽象基础类了!

  从理论层面打通了,下面上代码,首先看一下类装饰器 @six.add_metaclass(MetaClass) 的用法,在下面的代码中,我们希望声明类 MyClass 的metaclass是类 Meta ,注意类 Meta 需要是一个metaclass。

import six

@six.add_metaclass(Meta)
class MyClass(object):
pass

在Python 3 等价于

import six

class MyClass(object, metaclass = Meta):
pass

在Python 2.x (x >= 6)中等价于

import six

class MyClass(object):
__metaclass__ = Meta
pass

或者直接用引入装饰器的目的:

import six

class MyClass(object):
pass
MyClass = six.add_metaclass(Meta)(MyClass)

  类装饰器 @six.add_metaclass(MetaClass) 的作用是在不同版本的Python之间提供一个优雅的声明类的metaclass的手段,事实上不用它也可以,只是使用了它代码更为整洁明了。

  下面结合一个特殊的metaclass即 abc.ABCMeta 来看一段代码:

import abc
import six @six.add_metaclass(abc.ABCMeta)
class PluginBase(object): @abc.abstractmethod
def func_a(self,data):
"""
an abstract method need to be implemented
"""
@abc.abstractmethod
def func_b(self,output, data):
"""
another abstract method need to be implemented
""" class RegisteredImplementation(object): def func_c(self, data):
print "Method in third-party class, "+ str(data) class SubclassImplementation(PluginBase): def func_a(self,data):
print "Overriding func_a, "+ str(data) def func_b(self,data):
print "Overriding func_b, "+ str(data) def func_d(self, data):
print data PluginBase.register(RegisteredImplementation) if __name__=='__main__':
for sc in PluginBase.__subclasses__():
print "subclass of PluginBase: " + sc.__name__
print("")
print issubclass(RegisteredImplementation, PluginBase)
print isinstance(RegisteredImplementation(), PluginBase)
print issubclass(SubclassImplementation, PluginBase)
print("")
obj1 = RegisteredImplementation()
obj1.func_c("It's right!")
print("")
obj2 = SubclassImplementation()
obj2.func_a("It's right!")
print ""

  上面这端代码的含义是:

  声明一个metaclass是 abc.ABCMeta 的抽象基类 PluginBase ,为其定义两个抽象方法,等待派生类的实现。接着定义了一个第三方类 RegisterdImplementation ,将其注册为类 PluginBase 的虚拟子类。再定义一个子类 SubclassImplementation 直接继承自抽象基类 PluginBase 。

  接着进行试验,运行结果如下:

subclass of PluginBase: SubclassImplementation

True
True
True Method in third-party class, It's right! Overriding func_a, It's right!

  从运行的结果我们可以看出:

  虚拟子类不算做直接继承子类,因此可以不实现抽象基类 PluginBase 的任何方法;但直接继承的子类 SubclassImplementation 必须完全实现抽象基类的抽象方法才能够实例化(这里可以注释掉 26 - 30 行的代码实验)。

  同时,不论是虚拟子类还是直接继承子类,issubclass()和issubinstance()判断他们与抽象基类的关系时都返回真值。

Python装饰器、metaclass、abc模块学习笔记的更多相关文章

  1. python 装饰器和 functools 模块

    转自:http://blog.jkey.lu/2013/03/15/python-decorator-and-functools-module/ 什么是装饰器? 在 python 语言里第一次看到装饰 ...

  2. python装饰器学习详解-函数部分

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 最近阅读<流畅的python>看见其用函数写装饰器部分写的很好,想写一些自己的读书笔记. ...

  3. Python 装饰器学习心得

    最近打算重新开始记录自己的学习过程,于是就捡起被自己废弃了一年多的博客.这篇学习笔记主要是记录近来看的有关Python装饰器的东西. 0. 什么是装饰器? 本质上来说,装饰器其实就是一个特殊功能的函数 ...

  4. Python 装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 1 2 3 4 5 6 7 8 # -*- c ...

  5. (转载)Python装饰器学习

    转载出处:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方 ...

  6. Python装饰器学习

    Python装饰器学习(九步入门)   这是在Python学习小组上介绍的内容,现学现卖.多练习是好的学习方式. 第一步:最简单的函数,准备附加额外功能 ? 1 2 3 4 5 6 7 8 # -*- ...

  7. Python装饰器模式学习总结

    装饰器模式,重点在于装饰.装饰的核心仍旧是被装饰对象. 类比于Java编程的时候的包装模式,是同样的道理.虽然概念上稍有不同但是原理上还是比较相近的.下面我就来谈一谈我对Python的装饰器的学习的一 ...

  8. python 装饰器、递归原理、模块导入方式

    1.装饰器原理 def f1(arg): print '验证' arg() def func(): print ' #.将被调用函数封装到另外一个函数 func = f1(func) #.对原函数重新 ...

  9. Python 日期时间处理模块学习笔记

    来自:标点符的<Python 日期时间处理模块学习笔记> Python的时间处理模块在日常的使用中用的不是非常的多,但是使用的时候基本上都是要查资料,还是有些麻烦的,梳理下,便于以后方便的 ...

随机推荐

  1. 每日英语:Patent Wars Erupt Again in Tech Sector

    The long-running patent war among the technology industry's heavyweights just grew a whole lot bigge ...

  2. 玩转Bootstrap(JS插件篇)-第1章 模态弹出框 :1-1导入JavaScript插件

    导入JavaScript插件 Bootstrap除了包含丰富的Web组件之外,如前面介绍的下拉菜单.按钮组.导航.分页等.他还包括一些JavaScript的插件. Bootstrap的JavaScri ...

  3. FIDDLER的使用方法及技巧总结(连载一)FIDDLER快速入门及使用场景

    FIDDLER的使用方法及技巧总结 一.FIDDLER快速入门及使用场景 Fiddler的官方网站:http://www.fiddler2.com Fiddler的官方帮助:http://docs.t ...

  4. html+css+jQuery+JavaScript实现tab自动切换功能

    tab1.html内容 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> & ...

  5. loadrunner文本检查点

    将脚本切换回代码界面, 在光标闪烁的上行,添加如下的代码: 添加的代码根据你检查的方式不同而不同, 你可以选择其中之一即可. 代码一: web_reg_find("Text=Payment ...

  6. 猥琐百度杯猥琐CTF

    其实不难,但是作为我这个代码菜鸡+脑洞菜鸡+黑阔菜鸡而言确实挺难. 题目源码: <?php error_reporting(0); session_start(); require('./fla ...

  7. spring boot模仿reponseBody注解,自定义注解,返回值加上元数据

    简介 ResponseBody是通过RequestResponseBodyMethodProcessor起作用的. 我们的做法是写一个包装类,替换掉他 问题:怎么替换呢? 取出 Spring的List ...

  8. 【Unity】角色沿路线移动/朝着目标移动

    先在场景中放置一连串物体作为角色移动路线的关键点,可以把关键点的触发器Trigger拉得大一些方便角色接触到(如酷跑/赛车类项目可以把关键点的触发器做成拦截整个道路的墙面形状).让角色从开始位置朝着第 ...

  9. [设备]Linux设备是否可以被多个进程或者线程同时Open?

    当然可以 只要底层driver没有对重复打开做特殊处理,一般都可以被两个进程open 那当两个进程同时打开一个设备,当此设备收到数据时,怎么能保证每个进程都能收到数据?

  10. java读取配置文件内容

    利用com.typesafe.config包实现 <dependency> <groupId>com.typesafe</groupId> <artifact ...