文档章节

趣玩Python之装饰器详解

水果糖
 水果糖
发布于 2016/08/10 16:02
字数 1702
阅读 156
收藏 3

    装饰器,顾名思义为某种事物添加一个装饰;其实就是对函数进行“闭包”处理,从而隐藏复杂的逻辑实现、日志记录、性能测试、保持事务的一种方法。在面向切面需求中可以经常看到。

    用通俗语来说:“装饰器其实就是相当于一个包裹;同样是运行你的函数,只不过在运行你的函数之前与之后,装饰器(函数)可以进行一些上述相关操作而已,而这些操作能方便你最小化修改代码。”

 

下面我们看一个简单的例子:

装饰器 ① (最简单的装饰器)

#-*- coding:utf-8 -*-

def main(func):
    def work():
        print "装饰器开始运行"
        func()
        print "装饰器结束运行"
    return work


@main
def abc():
    print "     Candy's Comming!"

#上面等效 abc = main(abc)


abc()

 

输出结果:

C:\Python\python.exe C:/main.py

装饰器开始运行
     Candy's Comming!
装饰器结束运行

Process finished with exit code 0

 

    请将上述代码复制到您的IDE内并运行! 11 行的装饰器代码 等效于 abc = main(abc) ;这样我们就构造了abc函数运行的顺序,并用print函数将其运行顺序记录了下来。

    如果对此有疑惑,你在print abc.__name__ 的时候就能发现,abc.__name__变成了work。语法糖就是如此的奇妙。

    如果你理解了第一个例子,我们就来看看接下来如何装饰有参数的函数。

 

装饰器 ② (带参数的装饰器)

#-*- coding:utf-8 -*-

def main(func):
    def work(*args):
        print "装饰器开始运行"
        func(*args)
        print "装饰器结束运行"
    return work


@main
def abc(Say = ""):
    print "     %s's Comming!" % Say


abc("Candy")

 

运行结果如下:

C:\Python\python.exe C:/main.py

装饰器开始运行
     Candy's Comming!
装饰器结束运行

Process finished with exit code 0

 

    由于装饰器装饰过后,实际abc是work的引用。所以我们调用abc(string)这样的函数实际上是调用work函数;而work函数运行后,拿到Main函数的参数func作为函数、自己的参数*args作为func的参数执行。

    所以,结果输出 : "Candy' Comming!"

 

装饰器 ③:(类方法实现的装饰器)

# -*- coding:utf-8 -*-

class Main(object):

    @classmethod
    def Method(self,Say = "Hello"):
        def main(func):
            def work(*args,**kwargs):
                print Say
                print "装饰器开始运行..."
                result = func(*args,**kwargs)
                print "装饰器结束运行..."
                return result
            return work
        return main

@Main.Method("hello world!")
def abc(a,b):
    print abc.__name__
    return a + b

print "打印运行结果:%s" % abc(1,2)

 

运行结果如下:

C:\Python\python.exe C:/main.py

hello world!
装饰器开始运行...
work
装饰器结束运行...
打印运行结果:3

Process finished with exit code 0

 

    注意:由于类方法不能直接被类本身调用,所以我们需要在方法之前加上classmethod装饰,这样就可以被类本身所调用了。而这个方法由于是用C来实现的,如果有需要的同学可以参考Python源码实现。

    既然可以直接用类调用Method方法了,那么装饰器就可以直接写成 classname.Method(*args,**kwargs);我们给装饰器传入一个参数(注:给被装饰的函数传参与给装饰器传参是不同的)Say = "Hello world" ;然后Method方法返回了一个main(abc)这样的引用给abc,这时abc就相当于main函数了。这时我们再abc并给它传入2个参数(1,2),这就相当于运行了main函数内部的代码并且把参数1与2传入给*args和**kwargs。然后就开始正常运行内部带代码,打印、返回结果.....

    这里还有一个地方需要注意的是,在23行 print 关键字打印结果时,如果你用的不是'%'而是','的话,这将会导致运行结果与上述不一致。至于为什么不一致,就当Candy留给大家做观后思考吧! ^  _  ^

 

装饰器 ④:(为类方法提供参数)

#-*- coding:utf-8 -*-

class Flask:
    @classmethod
    def route(self,View_Route = '/',Method = "GET"):
        def wrap(cls):
            def work(*args,**kwargs):
                print "请求路由为:",View_Route
                print "请求方法为:",Method
                print "装饰器开始运行..."
                obj = cls(*args, **kwargs)
                print "装饰器结束运行..."
                return obj
            return work
        return wrap


@Flask.route("/",Method="POST")
class main(object):
    def __init__(self):
        print "Main被初始化"
        print "Main初始化结束"

    def work(self):
        print "main.work()被调用..."

m = main()
m.work()

 

运行结果如下:

C:\Python\python.exe C:/main.py

请求路由为: /
请求方法为: POST
装饰器开始运行...
Main被初始化
Main初始化结束
装饰器结束运行...
main.work()被调用...

Process finished with exit code 0

 

    上述代码是模仿Flask路由方法写的实例;如果你看过前三个装饰器的实例应该就会知道,类在初始化之前将调用我们的Flask.route装饰器并带上2个参数(请求路由 "/" 与 请求方法" POST");而route方法在接受到2个参数后返回一个wrap函数对象,也就是说在m = main()这一行,main()不是一个class而是一个wrap函数;这一行相当于运行wrap(),此时将上面被装饰的class main实例化后返回一个对象赋值给m,再由m来调用class main的方法work()进行打印输出。

 

    装饰器在装饰类与装饰函数时大致是一致的,唯一的区别在于类需要初始化而函数不需要。也正应为如此,导致大家在理解类装饰器的时候对某些运行步骤有些难以理解也实属正常。这里也只是浅显的点了一下装饰类即可,因为在大型与超大型项目程序设计中,大量的类装饰器是很难见到的。

 

  ————————————————————华丽的分割线————————————————————

 

    以上就是装饰器实现闭包的实际运行过程,但是这不是一个"无污染"的闭包;因为在被装饰的函数内打印自己的 __name__ 内部变量时显示的不是被装饰函数的名字,从而导致原函数的相关信息在闭包构造期间就被取代了。

    这时,我们可以使用一个functiontools 模块内置wrap这个装饰器将func函数再装饰一次。从而将在work函数内运行的func相关属性修改回来。源代码中是使用偏函数partial来构造的,而partial又是内置方法使用C语言来实现的。所以在此我们只需要知道运行流程是怎样的即可,具体实例可以参考官方。

    有兴趣的同学也可以去看看functiontools模块,有不懂的同学可以留言与我进行交流。

    由于在匆促之间完成,文字、逻辑之间难免有错误!欢迎大家指正! ^ _ ^

 

© 著作权归作者所有

共有 人打赏支持
水果糖
粉丝 15
博文 158
码字总数 52482
作品 0
深圳
程序员
私信 提问
转载:唐磊的个人博客《python中decorator详解》【转注:深入浅出清晰明了】

转载请注明来源:唐磊的个人博客《python中decorator详解》 前面写python的AOP解决方案时提到了decorator,这篇文章就详细的来整理下python的装饰器——decorator。 python中的函数即objects...

laugh2last
2015/08/17
0
0
Python3 与 C# 扩展之~基础拓展

上次知识回顾:https://www.cnblogs.com/dotnetcrazy/p/9278573.html 代码裤子:https://github.com/lotapp/BaseCode 在线编程:https://mybinder.org/v2/gh/lotapp/BaseCode/master 在线预览......

鲲逸鹏
07/19
0
0
设计模式之:理解 Python 中的装饰器

1、问题 文章先由stackoverflow上面的一个问题引起吧,如果使用如下的代码: @makebold @makeitalic def say(): return "Hello" 打印出如下的输出: <b><i>Hello<i></b> 你会怎么做?最后给出......

大数据之路
2013/07/22
0
0
D04-C语言基础学PYTHON

C语言基础学习PYTHON——基础学习D04                          20180810内容纲要:   1 内置函数   2 装饰器   3 生成器   4 迭代器   5 软件目录结构规...

m1racle
08/14
0
0
每周一个 Python 模块 | functools

functools 是 Python 中很简单但也很重要的模块,主要是一些 Python 高阶函数相关的函数。 该模块的内容并不多,看 官方文档 也就知道了。 说到高阶函数,这是函数式编程范式中很重要的一个概...

yongxinz
11/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

聊聊我怎么系统学习Linux技能并快速提高的

聊聊我怎么系统学习Linux技能并快速提高的 随着电子信息科技时代的发展,学会使用计算机在我们的生活中成为了必不可少的一项技能。而作为计算机中的三大操作系统之一的Linux更是饱受计算机爱...

linuxCool
10分钟前
1
0
Android/Java 获取一个byte[]的真实编码,用于解决乱码问题

来源地址:https://blog.csdn.net/qq_31384551/article/details/81627840 一个byte数组,byte[] buf,是由一个字符串转换来的,如何判断字符串使用的是什么编码? Mozilla的一个组件提供了相应...

她叫我小渝
19分钟前
0
0
使用spring-retry实现重试机制

我们经常会遇到一些场景,需要重试当前的操作,比如:文件上传失败,重新上传;微信accesstoken获取失败,重新获取等等的场景问题。那么今天就说一种可以实现此种机制的工具包:spring-retr...

哥本哈根的小哥
21分钟前
0
0
kubernetes notes

Error from server (ServerTimeout): error when creating "mysql.yaml": No API token found for service account "default", retry after the token is automatically created and added t......

kut
42分钟前
2
0
代理模式---静态代理

package com.atguigu.java; //接口的应用:代理模式---静态代理。 public class ProxyTest { public static void main(String[] args) { NetWork work = new RealServer(); ProxyServer pro......

architect刘源源
57分钟前
2
1

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部