文档章节

趣玩Python之装饰器详解

水果糖
 水果糖
发布于 2016/08/10 16:02
字数 1702
阅读 144
收藏 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
博文 157
码字总数 52251
作品 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
python 装饰器及标准库functools中的wraps

最近在看 flask的视图装饰器 时,忽然想起预(复)习一下python的装饰器. 这里有一篇比较好的讲解装饰器的书写的 Python装饰器学习(九步入门) . 这里不单独记录装饰器的书写格式了,重点是工作...

L很失败L
2015/08/20
0
0
Python基础——装饰器、模块(0417)

一、Python基础——复习 1、字符串的常用操作 2、列表的常用操作 3、字典的常用操作 二、Python——装饰器:函数可以是变量 1、Python是一种面向对象的编程语言,在Python中所有的都可以是Pyt...

python初雪之路
04/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

linux运维人员必会运维工具

linux运维人员必会开源运维工具体系 说明:不同的技术人员,不同的阶段确定知识边界非常重要,否则,就像马拉车,不知道终点在哪,累死也达不到目标。例如拿8K要学多少,拿15K要学多少。一个...

寰宇01
13分钟前
2
0
10大PHP比特币开源项目

如果你是一个Phper,如果你希望学习区块链,那么本文列出的 10个开源的Php比特币项目,将有助于你了解在自己的应用中 如何加入对比特币的支持。 如果你希望快速掌握使用Php对接比特币钱包的方...

汇智网教程
34分钟前
1
0
springclould feign客户端添加全局参数

用springclould feign作为调用服务的客户端,一般来说参数可以写在feignclient的方法参数里 有时需要所有feign请求都统一添加一些参数,例如token用于鉴权等,可以这样做: 添加一个配置类,...

canneljls
35分钟前
1
0
win32截屏并rgb24转yuv420

//最终f的内存布局为BGRA格式,需要保证buf长度足够(>w*h*4)void ScreenCap(void* buf, int w, int h){ HWND hDesk = GetDesktopWindow(); HDC hScreen = GetDC(hDesk); ......

styleman
今天
1
0
php输出mysql取出的中文为??的问题

解决方法: @ $db=new mysqli(DB_HOST,DB_USER,DB_PASSWORD,DB_DB); $db->query("set names utf8");//添加此语句,可以解决问题...

Aomo
今天
1
2

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部