python装饰器详解

Bill78 等级 683 0 0
你会Python嘛?
我会!
那你给我讲下Python装饰器吧!
Python装饰器啊?我没用过哎 

以上是我一个哥们面试时候发生的真实对白。

----------------------------------------------分割线------------------------------------------------------------------------------

简言之,python装饰器就是用于拓展原来函数功能的一种函数,这个函数的特殊之处在于它的返回值也是一个函数,使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
一般而言,我们要想拓展原来函数代码,最直接的办法就是侵入代码里面修改,例如:

import time
def func():
    print("hello")
    time.sleep(1)
    print("world") 

这是我们最原始的的一个函数,然后我们试图记录下这个函数执行的总时间,那最简单的做法就是:

#原始侵入,篡改原函数
import time
def func():
    startTime = time.time()

    print("hello")
    time.sleep(1)
    print("world")
    endTime = time.time()

    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs) 

但是如果你的Boss在公司里面和你说:“小祁,这段代码是我们公司的核心代码,你不能直接去改我们的核心代码。”那该怎么办呢,我们仿照装饰器先自己试着写一下:

#避免直接侵入原函数修改,但是生效需要再次执行函数
import time

def deco(func):
    startTime = time.time()
    func()
    endTime = time.time()
    msecs = (endTime - startTime)*1000
    print("time is %d ms" %msecs)


def func():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
    f = func
    deco(f)#只有把func()或者f()作为参数执行,新加入功能才会生效
    print("f.__name__ is",f.__name__)#f的name就是func 

这里我们定义了一个函数deco,它的参数是一个函数,然后给这个函数嵌入了计时功能。然后你可以拍着胸脯对老板说,看吧,不用动你原来的代码,我照样拓展了它的函数功能。
然后你的老板有对你说:“小祁,我们公司核心代码区域有一千万个func()函数,从func01()到func1kw(),按你的方案,想要拓展这一千万个函数功能,就是要执行一千万次deco()函数,这可不行呀,我心疼我的机器。”
好了,你终于受够你老板了,准备辞职了,然后你无意间听到了装饰器这个神器,突然发现能满足你闫博士的要求了。
我们先实现一个最简陋的装饰器,不使用任何语法糖和高级语法,看看装饰器最原始的面貌:

#既不需要侵入,也不需要函数重复执行
import time

def deco(func):
    def wrapper():
        startTime = time.time()
        func()
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func():
    print("hello")
    time.sleep(1)
    print("world")

if __name__ == '__main__':
    f = func #这里f被赋值为func,执行f()就是执行func()
    f() 

这里的deco函数就是最原始的装饰器,它的参数是一个函数,然后返回值也是一个函数。其中作为参数的这个函数func()就在返回函数wrapper()的内部执行。然后在函数func()前面加上@deco,func()函数就相当于被注入了计时功能,现在只要调用func(),它就已经变身为“新的功能更多”的函数了。
所以这里装饰器就像一个注入符号:有了它,拓展了原来函数的功能既不需要侵入函数内更改代码,也不需要重复执行原函数。

#带有参数的装饰器
import time

def deco(func):
    def wrapper(a,b):
        startTime = time.time()
        func(a,b)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))

if __name__ == '__main__':
    f = func
    f(3,4)
    #func() 

然后你满足了Boss的要求后,Boss又说:“小祁,我让你拓展的函数好多可是有参数的呀,有的参数还是个数不定的那种,你的装饰器搞的定不?”然后你嘿嘿一笑,深藏功与名!

#带有不定参数的装饰器
import time

def deco(func):
    def wrapper(*args, **kwargs):
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
    return wrapper


@deco
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def func2(a,b,c):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b+c))


if __name__ == '__main__':
    f = func
    func2(3,4,5)
    f(3,4)
    #func() 

最后,你的老板说:“可以的,小祁,我这里一个函数需要加入很多功能,一个装饰器怕是搞不定,装饰器能支持多个嘛"
最后你就把这段代码丢给了他:

#多个装饰器

import time

def deco01(func):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        startTime = time.time()
        func(*args, **kwargs)
        endTime = time.time()
        msecs = (endTime - startTime)*1000
        print("time is %d ms" %msecs)
        print("deco01 end here")
    return wrapper

def deco02(func):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        func(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def func(a,b):
    print("hello,here is a func for add :")
    time.sleep(1)
    print("result is %d" %(a+b))



if __name__ == '__main__':
    f = func
    f(3,4)
    #func()

'''
this is deco01
this is deco02
hello,here is a func for add :
result is 7
deco02 end here
time is 1003 ms
deco01 end here
''' 

多个装饰器执行的顺序就是从最后一个装饰器开始,执行到第一个装饰器,再执行函数本身。

盗用评论里面一位童鞋的例子:

def dec1(func):  
    print("1111")  
    def one():  
        print("2222")  
        func()  
        print("3333")  
    return one  

def dec2(func):  
    print("aaaa")  
    def two():  
        print("bbbb")  
        func()  
        print("cccc")  
    return two  

@dec1  
@dec2  
def test():  
    print("test test")  

test() 

输出:

aaaa  
1111  
2222  
bbbb  
test test  
cccc  
3333 

装饰器的外函数和内函数之间的语句是没有装饰到目标函数上的,而是在装载装饰器时的附加操作。

17~20行是装载装饰器的过程,相当于执行了test=dect1(dect2(test)),此时先执行dect2(test),结果是输出aaaa、将func指向函数test、并返回函数two,然后执行dect1(two),结果是输出1111、将func指向函数two、并返回函数one,然后进行赋值。

用函数替代了函数名test。 22行则是实际调用被装载的函数,这时实际上执行的是函数one,运行到func()时执行函数two,再运行到func()时执行未修饰的函数test。

参考:Python 装饰器:http://python.jobbole.com/82344/

python装饰器详解

本文转自 https://blog.csdn.net/xiangxianghehe/article/details/77170585,如有侵权,请联系删除。

收藏
评论区

相关推荐

Python的环境搭建和下载
Python是一个跨平台、可移植的编程语言,因此可在windows、Linux和Mac OS X系统中安装使用。 安装完成后,你会得到Python解释器环境,可以通过终端输入python命令查看本地是否已经按照python以及python版本。这里有一点需要注意的是,如果没有将python的安装目录添加到环境变量中,会报错(python不是内部命令或外部命
python装饰器详解
你会Python嘛? 我会! 那你给我讲下Python装饰器吧! Python装饰器啊?我没用过哎 以上是我一个哥们面试时候发生的真实对白。 \分割线
Python基础1——变量、判断、循环、字符串、列表
1 认识python 1.1 认识python计算机识别机器语言,机器语言由二进制0和1组成计算机要执行高级语言,转换方式 1. 编译 : C语言、C++、java。 一次性编译成可执行文件2. 解释 : 一行一行地解释python是解释型语言python解释器、pycharm编辑器举个例子:要给工地煮饭编译:把饭菜都做好,做成盒饭(.exe,.class
20155117王震宇网络对抗
##扩展Burp代理 环境 * java环境运行Burpsuite * Burpsuite * Jython -java编写的python解释器 BurpSuite 插件扩展开发所支持的编程语言有 Java 和 Python,Python 开发的插件需要java编写的解释器Jpython。 SDK: * 开发文档: BurpSuit
Ubuntu部署shadowsocks服务端
服务器基本要求: Python2.7 1 安装pip环境 apt–get install python–gevent python–pip 2 安装shadowsocks pip install shadowsocks #升级命令 pip install –upgrade shadowsocks 3 安装加密用的库 apt–get insta
Atom的python插件和常用插件
python: simplified-chinese-menu:中文汉化(英文差的) 代码高亮:Atom自带 自动补全:autocomplete-python 语法检查:linter-flake8 定义跳转:python-tools 代码运行:atom-runner (只能输出,不能输入),atom-python-run(Windows,可以输入
CentOS升级Python到2.7版本
查看python的版本 python -V Python 2.4.3 1.先安装GCC yum -y install gcc 2.下载Python-2.7.2 wget http://python.org/ftp/python/2.7.2/Python-2.7.2.tar.bz2 3.解压Python-2.7.2
Flask教程(十五)日志
### 软硬件环境 * windows 10 64bit * anaconda3 with python 3.7 * pycharm 2020.1.2 * flask 1.1.2 ### 简介 `flask`日志使用标准的`python``logging`。所有与`flask`相关的消息都用`ap
Linux下Python编程
linux下python编程可以使用sublime-text3来作为编辑器,很方便,而且linux默认就是有python2和python3环境安装的,我们只要安装sublime-text3即可进行python编程 sudo update-alternatives --install /usr/bin/python python /usr/bin/p
Python 并行分布式框架之 PP
PP ([Parallel Python](https://www.oschina.net/action/GoToLink?url=http%3A%2F%2Fwww.parallelpython.com%2F))是基于Python的一个轻量级的,提供在SMP(多处理器或者多核系统)或者集群环境中并行执行Python代码的机制。 最简单和最常见的并行方式
Python内置简易HTTP服务器的使用
Python内置了一个简单的HTTP服务器,命令行切换到放置网页文件(例如index.html)的目录,只需要在命令行下面敲一行命令,一个HTTP服务器就起来了: **1)Python2.x** : python -m SimpleHTTPServer 8080 **2)Python3.x** python -m http.server 80
Python容器
列表 -- 列表的元素可以修改的,通过直接赋值就能修改列表的元素。 使用 \[ \] 创建列表: weekdays = \['monday','tuesday','wednesday','thursday','friday'\] 也可以使用 list() 创建空列表: weekdays = list() 使用 append() 在列表尾部添加元素
Python技巧之使用“faulthandler”标准库模块
Python 3.3有一个标准库模块,用于展示回溯,即使Python“dies”。例如,下面为一个段错误在启用错误操作器前后的标准输出: $ python3 -c "import ctypes; ctypes.string_at(0)" Segmentation fault $ python3 -q -X faulthan
Python解释器和IPython
简介 == 今天给大家介绍一下Python的一个功能非常强大的解释器IPython。虽然Python本身自带解释器,但是相对而言IPython的功能更加的强大。 Python解释器 ========= Python是自带解释器的,我们在命令行输入python即可进入python的解释器环境: $> pythonPython 2.7.15 (d
Tensorflow开篇:环境安装1—Anaconda3
​Anaconda是一个Python科学计算环境,提供了很多常用的Python库,例如:  numpy,scipy, matplotlib等等。自带的包管理器conda也很强大,可以方便地安装各种Python库。 Anaconda3-4.1.1-Windows-x86_64.exe --python2.7 Ana