文档章节

趣玩Python之装饰器详解

水果糖
 水果糖
发布于 2016/08/10 16:02
字数 1702
阅读 134
收藏 3
点赞 1
评论 0

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

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

 

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

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

#-*- 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
博文 156
码字总数 51701
作品 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 在线预览......

鲲逸鹏
今天
0
0
设计模式之:理解 Python 中的装饰器

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

大数据之路
2013/07/22
0
0
深入浅出 Python 装饰器:16 步轻松搞定 Python 装饰器

Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西。虽然好像,他们要干的事都很相似——都是想...

大数据之路
2015/07/12
0
2
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
Python装饰器AOP 不定长参数 鸭子类型 重载(三)

1 可变长参数与关键字参数 代表任意长度可变参数 代表关键字参数 用和只是为了方便并没有强制使用它们. 缺省参数即是调用该函数时,缺省参数的值若未被传入,则传入默认预设的值。 注意 : 须...

善良小郎君
06/17
0
0
Django中六个常用的自定义装饰器

装饰器作用 decorator是当今最流行的设计模式之一,很多使用它的人并不知道它是一种设计模式。这种模式有什么特别之处? 有兴趣可以看看Python Wiki上例子,使用它可以很方便地修改对象行为,...

j_hao104
07/03
0
0
Python 装饰器的一个妙用

之前在上一篇文章《理解 Python 中的装饰器》中有过这块的理论介绍与例子,不知道你还有没有印象。装饰器在 python 中用的相当广泛,如果你用过 python 的一些 web 框架,那么一定对其中的 ...

大数据之路
2013/12/03
0
0
一个月入门Python爬虫,快速获取大规模数据

数据是创造和决策的原材料,高质量的数据都价值不菲。而利用爬虫,我们可以获取大量的价值数据,经分析可以发挥巨大的价值,比如: 豆瓣、知乎:爬取优质答案,筛选出各话题下热门内容,探索...

Python开发者
04/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

全新内存布局Android5 for one x

众所周知Android5.0默认ART模式,运行速度加倍,软件占用内存也加倍,我们one x这种元老机采用旧的内存布局,data空间2g ART模式下安装几个软件也就不够用了。最近逛国外的xda论坛,发现有大...

CrazyManDF
9分钟前
0
0
web3j转账

 web3 转账功能   为了完成以太坊交易,必须有几个先决条件   1、对方的以太坊地址   2、确定要转账的金额   3、自己地址的转账权限   4、大于转账金额的以太币,以太币转账其实就...

智能合约
10分钟前
0
0
10.28 rsync工具介绍 , rsync常用选项, rsync通过ssh同步

rsync远程同步 重点!重点!!重点!!! 例子 • rsync -av /etc/passwd /tmp/1.txt • rsync -av /tmp/1.txt 192.168.188.128:/tmp/2.txt rsync格式 • rsync [OPTION] … SRC DEST • rs......

Linux_老吴
24分钟前
0
0
iis php 环境搭建,非常详细的教程

准备篇 一、环境说明: 操作系统:Windows Server 2016 PHP版本:php 7.1.0 MySQL版本:MySQL 5.7.17.0 二、相关软件下载: 1、PHP下载地址: http://windows.php.net/downloads/releases/ph...

T_star
26分钟前
0
0
Day35 rsync通过服务同步

rsync通过服务同步 rsyncd.conf配置文件详解 port:指定在哪个端口启动rsyncd服务,默认是873端口。 log file:指定日志文件。 pid file:指定pid文件,这个文件的作用涉及服务的启动、停止等...

杉下
31分钟前
1
0
【最新最全】为 iOS 和 Android 的真机和模拟器编译 Luajit 库

编译 Luajit 库,的确是一个挑战。因为官网的教程,在当前版本的 Xcode 和 NDK 环境中,已经不适用了。以前只是编译了适用于真机的 Luajit 库。最近在尝试编译模拟器 Luajit 库,就顺便梳理了...

ios122
31分钟前
0
0
rsync至ssh同步

rsync: 文件同步工具,可实现“增量拷贝”;使用yum安装rsync包 常用选项:-a=-rtplgoD (-r同步目录,-t保持文件的时间属性,-p保持文件的权限属性,-l保持软连接,-g保持文件的属组,-o保持...

ZHENG-JY
37分钟前
0
0
TradingView 学习笔记

#前言 公司最后需要使用TradingView展示K线图走势。由于之前没接触过,拿到文档时一脸蒙逼。还好找到二篇文章+Demo代码,直接改改就行了。 #被批 由于上面的懵懂,有个问题困扰4个小时没解决...

hihubs
37分钟前
0
0
10.28 rsync工具介绍~10.31 rsync通过ssh同步

rsync命令是一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件。rsync使用所谓的“rsync算法”来使本地和远程两个主机之间的文件达到同步,这个算法只传送两个文件的不同部分,而...

洗香香
39分钟前
1
0
卷积为什么要旋转180度

参考《最容易理解的对卷积(convolution)的解释》 https://blog.csdn.net/bitcarmanlee/article/details/54729807 这篇博客详细讲解了“卷积”,提及了为什么要反转180度,我简述下。 1.卷积的...

datadev_sh
48分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部