Python Decorator的设计模式演绎过程解析

目录
  • 关于代理模式、装饰模式
  • Python中的代理/装饰
    • 还有什么不理想的地方呢?
  • 补充

关于代理模式、装饰模式

设计模式中经常提到的代理模式、装饰模式,这两种叫法实际上是说的同一件事,只是侧重点有所不同而已。

这两者都是通过在原有对象的基础上封装一层对象,通过调用封装后的对象而不是原来的对象来实现代理/装饰的目的。

例如:(以Java为例)

public class CountProxy implements Count {
    private CountImpl countImpl;
    public CountProxy(CountImpl countImpl) {
        this.countImpl = countImpl;
    }
    @Override
    public void queryCount() {
        System.out.println("事务处理之前");
        // 调用委托类的方法;
        countImpl.queryCount();
        System.out.println("事务处理之后");
    }
    @Override
    public void updateCount() {
        System.out.println("事务处理之前");
        // 调用委托类的方法;
        countImpl.updateCount();
        System.out.println("事务处理之后");
    }
}

在这个例子中CountProxy是对CountImpl的封装。

使用者通过CountProxy.queryCount方法来调用CountImpl.queryCount方法,这被称为代理,即CountProxy是代理类,CountImpl是被代理类。

CountProxy.queryCount方法中,可以在CountImpl.queryCount方法调用之前和之后添加一些额外的操作,被称为装饰,即CountProxy是装饰类,CountImpl是被装饰类。

如果强调通过CountProxy 对CountImpl进行代理的作用,则称为代理模式;

如果强调通过CountProxy 对CountImpl增加额外的操作,则称为装饰模式;

不论是哪种称呼,其本质都在于对原有对象的封装。

其封装的目的在于增强所封装对象的功能或管理所封装的对象。

从上面的例子也可以发现,代理/封装所围绕的核心是可调用对象(比如函数)。

Python中的代理/装饰

Python中的可调用对象包括函数、方法、实现了__call__方法的类。

Python中的函数也是对象,可以作为高阶函数的参数传入或返回值返回。

因此,当代理/装饰的对象是函数时,可以使用高阶函数来对某个函数进行封装。

例如:

def query_count_proxy(fun, name, age):
    print('do something before')
    rv = fun(name, age)
    print('do something after')
    return rv
def query_count(name, age):
    print('name is %s, age is %d' % (name, age))
query_count_proxy(query_count, 'Lee', 20)

但是,这个例子中,query_count函数作为参数传入query_count_proxy函数中,并在query_count_proxy函数中被调用,其结果作为返回值返回。这就完成了代理的功能,同时,在调用query_count函数的前后,我们还增加了装饰代码。

但是,query_count_proxy的函数参数与query_count不一样了,理想的代理应该保持接口一致才对。

为了保持一致,我们可以利用高阶函数可以返回函数的特点来完成:

def query_count_proxy(fun):
    def wrapper(name, age):
        print('do something before')
        rv = fun(name, age)
        print('do something after')
        return rv
    return wrapper
def query_count(name, age):
    print('name is %s, age is %d' % (name, age))
query_count_proxy(query_count)('Lee', 20)

修改后的例子,query_count_proxy仅负责接受被代理的函数query_count作为参数,同时,返回一个函数对象wrapper作为返回值,真正的封装动作在wrapper这个函数中完成。

此时,如果调用query_count_proxy(query_count)就得到了wrapper函数对象,则,执行query_count_proxy(query_count)('Lee', 20)就相当于执行了wrapper('Lee', 20)

但是可以看到,query_count_proxy(query_count)('Lee', 20)这种使用方法,仍然不能保证一致。

为了保持一致,我们需要利用Python中对象与其名称可以动态绑定的特点。不使用query_count_proxy(quer_count)('Lee', 20)来调用代理函数,而是使用下面两句:

query_count = query_count_proxy(query_count)
query_count('Lee', 20)

执行query_count_proxy(query_count)生成wrapper函数对象,将这个对象通过query_count = query_count_proxy(query_count)绑定到query_count这个名字上来,这样执行query_count('Lee', 20)时,其实执行的是wrapper('Lee', 20)

这么做的结果就是:使用代理时调用query_count('Lee', 20)与不使用代理时调用query_count('Lee', 20)对使用者而言保持不变,不用改变代码,但是在真正执行时,使用的是代理/装饰后的函数。

这里,基本利用Python的高阶函数及名称绑定完成了代理/装饰的功能。

还有什么不理想的地方呢?

对,就是query_count = query_count_proxy(query_count),因为这句既不简洁,又属于重复工作。

Python为我们提供了语法糖来完成这类的tedious work。

方法就是:

@query_count_proxy
def query_count(name, age):
    return 'name is %s, age is %d' % (name, age)

query_count = query_count_proxy(query_count)就等同于在定义query_count函数的时候,在其前面加上@query_count_proxy

Python看到这样的语法,就会自动的执行query_count = query_count_proxy(query_count)进行name rebinding

补充

以上就是Python实现可调用对象装饰的核心。

可调用对象包括函数、方法、实现了__call__方法的类,上述内容只是针对函数来解释,对于方法、实现了__call__方法的类,其基本原理相同,具体实现略有差别。

以上就是Python Decorator的设计模式演绎过程解析的详细内容,更多关于Python Decorator设计模式的资料请关注我们其它相关文章!

(0)

相关推荐

  • python中的decorator的作用详解

    1.概念 装饰器(decorator)就是:定义了一个函数,想在运行时动态增加功能,又不想改动函数本身的代码.可以起到复用代码的功能,避免每个函数重复性编写代码,简言之就是拓展原来函数功能的一种函数.在python中,装饰器(decorator)分为 函数装饰器 和 类装饰器 两种.python中内置的@语言就是为了简化装饰器调用. 列出几个装饰器函数: 打印日志:@log 检测性能:@performance 数据库事务:@transaction URL路由:@post('/register')

  • Python decorator拦截器代码实例解析

    这个decorator是什么意思呢? 很简单,它接受一个log的参数,即传入logger对象.然后它会先把函数名打印出来,接下来,会利用enumerate函数,将args.kwargs的任意参数给展开,并按顺序输出.最后,它再将func以这些参数包装回去,继续进行函数的执行和结果返回. 例1 最简单 def get_text(name): return "hello, {0}".format(name) def p_decorate(func): def func_wrapper(na

  • python 装饰器(Decorators)原理说明及操作代码

    目录 1 必要的2个核心操作 1.1 核心操作1, 函数内部可以定义函数 1.2 核心操作2 函数可以作为对象被输入输出 1.2.1 核心操作2的前置条件,函数是对象 1.2.2函数作为输入 1.2.3 函数作为输出 2 尝试构造装饰器 3装饰器定义的简写 本文目的是由浅入深地介绍python装饰器原理 装饰器(Decorators)是 Python 的一个重要部分 其功能是,在不修改原函数(类)定义代码的情况下,增加新的功能 为了理解和实现装饰器,我们先引入2个核心操作: 1 必要的2个核心操

  • Python装饰器(decorator)定义与用法详解

    本文实例讲述了Python装饰器(decorator)定义与用法.分享给大家供大家参考,具体如下: 什么是装饰器(decorator) 简单来说,可以把装饰器理解为一个包装函数的函数,它一般将传入的函数或者是类做一定的处理,返回修改之后的对象.所以,我们能够在不修改原函数的基础上,在执行原函数前后执行别的代码.比较常用的场景有日志插入,事务处理等. 装饰器 最简单的函数,返回两个数的和 def calc_add(a, b): return a + b calc_add(1, 2) 但是现在又有新

  • 分析Python中设计模式之Decorator装饰器模式的要点

    先给出一个四人团对Decorator mode的定义:动态地给一个对象添加一些额外的职责. 再来说说这个模式的好处:认证,权限检查,记日志,检查参数,加锁,等等等等,这些功能和系统业务无关,但又是系统所必须的,说的更明白一点,就是面向方面的编程(AOP). 在Python中Decorator mode可以按照像其它编程语言如C++, Java等的样子来实现,但是Python在应用装饰概念方面的能力上远不止于此,Python提供了一个语法和一个编程特性来加强这方面的功能.Python提供的语法就是

  • Python Decorator的设计模式演绎过程解析

    目录 关于代理模式.装饰模式 Python中的代理/装饰 还有什么不理想的地方呢? 补充 关于代理模式.装饰模式 设计模式中经常提到的代理模式.装饰模式,这两种叫法实际上是说的同一件事,只是侧重点有所不同而已. 这两者都是通过在原有对象的基础上封装一层对象,通过调用封装后的对象而不是原来的对象来实现代理/装饰的目的. 例如:(以Java为例) public class CountProxy implements Count { private CountImpl countImpl; publi

  • python打包成so文件过程解析

    这篇文章主要介绍了python打包成so文件过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 wget https://bootstrap.pypa.io/get-pip.py python get-pip.py pip install cython 编写setput.py文件: setup.py文件内容如下: from distutils.core import setup from distutils.extension import

  • python文字转语音实现过程解析

    这篇文章主要介绍了python文字转语音实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 使用百度接口 接口地址 https://ai.baidu.com/docs#/TTS-Online-Python-SDK/top 安装接口 pip install baidu-aip from aip import AipSpeech """ 你的 APPID AK SK """ APP_ID =

  • python使用rsa非对称加密过程解析

    这篇文章主要介绍了python使用rsa非对称加密过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.安装rsa 支持python 2.7 或者 python 3.5 以上版本 使用豆瓣pypi源来安装rsa pip install -i https://pypi.douban.com/simple rsa 2.加密解密 2.1.生成公私钥对 import rsa # 1.接收者(A)生成512位公私钥对 # a. lemon_pub为

  • Python测试线程应用程序过程解析

    这篇文章主要介绍了Python测试线程应用程序过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在本章中,我们将学习线程应用程序的测试.我们还将了解测试的重要性. 为什么要测试? 在我们深入讨论测试的重要性之前,我们需要知道测试的内容.一般来说,测试是一种了解某些东西是如何运作的技术.另一方面,特别是如果我们谈论计算机程序或软件,那么测试就是访问软件程序功能的技术. 在本节中,我们将讨论软件测试的重要性.在软件开发中,必须在向客户端发布软

  • python全局变量引用与修改过程解析

    这篇文章主要介绍了python全局变量引用与修改过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.引用 使用到的全局变量只是作为引用,不在函数中修改它的值的话,不需要加global关键字.如: #! /usr/bin/python a = 1 b = [2, 3] def func(): if a == 1: print("a: %d" %a) for i in range(4): if i in b: print(&quo

  • Python namedtuple命名元组实现过程解析

    这篇文章主要介绍了Python namedtuple命名元组实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 命名元组(namedtuple)是一种带有属性的元组,它们是组合只读数据的很好的方式. 相比一般的元组,构造命名元组需要先导入namedtuple,因为它不在默认的命名空间里.然后通过名字和属性来定义一个命名元组.这会返回一个像类一样的对象,可以进行多次实例化. 命名元组可以被打包.解包以及做所有可以对普通元组做的事,并且还可

  • python自动化unittest yaml使用过程解析

    这篇文章主要介绍了python自动化unittest yaml使用过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在编写unittest自动化用例时,一个请求需要编写多条用例,而涉及的参数基本相同,这时候就会用到配置文件,可以把参数配置项统一管理,避免重复代码,也方便后期维护 此处用到的是yaml,首先需要安装yaml库,pip install yaml 安装成功后,脚本导入语句,import yaml,具体语法可参照如上入门教程 举例

  • Python Django源码运行过程解析

    目录 一.Django运行顺序 1.启动 1.1 命令行启动(测试服务器) 2.监听 2.1 runserver(测试服务器) 3.中间件的执行 本文只算是本人片面之言(当然也会借鉴网络上公开资料),而且技术含量比较低,内容质量也一般,大家仅限参考即可 如果对本文看不太懂,请先阅读后面文章,等都差不多看完再回顾来看 一.Django运行顺序 WSGI会不断监听客户端发送来的请求 先经过中间件进行分析验证处理 然后经过url分发与验证 视图层进行处理 再经过中间件进行分析验证处理 返回响应内容 1

  • Python脚本打包成可执行文件过程解析

    Python是一个脚本语言,被解释器解释执行.它的发布方式: .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库.(Python官方的各种安装包就是这样做的) .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库. 可执行文件:对于非码农用户或者一些小白用户,你让他装个Pyth

随机推荐