python学习之装饰器

4.9 装饰器

4.9.1 开放封闭原则

​ 1.对扩展是开放的:允许代码扩展、添加新功能。

​ 2.对修改是封闭的:不要改变源码。防止对函数内部进行修改,不能改变调用方式

4.9.2 装饰器初识

定义:在不改变原被装饰的函数的源代码以及调用方式下,为其添加一个额外的功能。

装饰器本身是一个函数

版本一:
import time

def index():
    time.sleep(2)
    print('装饰器')

def func(f):
    def inner():
        start_time = time.time()    #格林威治时间
        f()
        end_time = time.time()
        print(f"执行时间为{end_time - start_time}")
    return inner
index()
index = func(index)
index()   #实际执行的是inner()

上边两个 index() 输出的结果是不一样的,第二个index()的功能已经增加

这么做的优点在于:没有改变index的执行方式,如果整个py文件里调用了100次index(),只需要这一步函数,不重要其他的修改,就可以神不知鬼不觉地进行了功能上的升级。

版本二:带自由变量的装饰器
import time

def index():
    time.sleep(2)
    print('装饰器')

def func(f):
    f = index  # 自由变量 f 存在的内存地址与全局和临时空间并列,不会因为函数的结束而消失,对其的赋值必须是要修饰的函数的变量名,因为在内部函数时要执行,此时它接收到的时函数index指向的内存地址
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print(f"执行时间为{end_time - start_time}")

    return inner

index = func(index)
index()  #实际执行的是inner()
版本三:语法糖装饰器(标准版)

一般情况下,应该把装饰器函数放在需要加装饰器的函数之上

@装饰器名 放在需要加装饰器的函数名上边

# 语法糖装饰器
import time

# func装饰器
def func(f):
    def inner():
        start_time = time.time()
        f()
        end_time = time.time()
        print(f"执行时间为{end_time - start_time}")

    return inner

@func  # 等同于 index = func(index)
def index():
    time.sleep(2)
    print('装饰器')

def copare():
    time.sleep(2)
    print('比较用的')

index()  #实际执行的是inner()
copare()

4.9.3 带返回值的修饰器

如果原函数带返回值怎么办?对于前边介绍的几种装饰器实际运行时的index()其实执行的是inner()内部函数,而原来的index含糊的返回值实际上是给了f(),所以如果添加装饰器后,要保持原函数的返回值,就要把f()接受的数值当成inner的返回值,有点绕,代码如下:

import time

# func装饰器
def func(f):
    def inner():
        start_time = time.time()
        s = f()
        print(s)    #检验:f()实际上执行的是被装饰的原函数,是有返回值的
        end_time = time.time()
        print(f"执行时间为{end_time - start_time}")
        return s
    return inner

@func  # 等同于 index = func(index)
def index():
    time.sleep(2)
    print('装饰器')
    return 3
@func
def copare():
    time.sleep(2)
    print('比较用的')
    return 4

print(index())
print(copare())

#输出装饰器
3             #f()调用的函数的返回值
执行时间为2.0006275177001953
3
比较用的
4	         #f()调用的函数的返回值
执行时间为2.0006816387176514
4如下:


4.9.4带参数的装饰器

import time

# func装饰器
def func(f):   #这里的f是被修饰的函数
    def inner(*args,**kwargs):   #函数的定义,*,**的作用是聚合参数,这里使用动态参数是因为不知道被修饰函数到底有多少参数
        start_time = time.time()
        s = f(*args,**kwargs)    #含糊的执行,*,**的作用是打散参数
        end_time = time.time()
        print(f"执行时间为{args,kwargs}")
        return s      #返回被修饰函数的返回值
    return inner

@func  # 等同于 index = func(index)
def index(argv):
    time.sleep(2)
    print(argv)
    return 3
@func
def copare(argv):
    time.sleep(2)
    print(argv)
    return 4

print(index('haha'))
print(copare('xixi'))
#输出
haha
执行时间为(('haha',), {})
3
xixi
执行时间为(('xixi',), {})
4


4.9.5标准版装饰器

结构如下:

def wrapper(func):                	#func是被修饰的函数,wrapper是外层函数
    def inner(*args,**kwargs):    	#定义的额内层函数,*  **的作用是聚合参数
        '''执行被装饰函数之前的操作'''
        ret = func                	#被修饰函数的执行,可以使用*  **打散函数
        '''执行被装饰函数之后的操作'''
        return ret                	#返回被修饰函数的返回值
    return inner                  	#返回被修饰函数

4.9.6 带参数的装饰器***

def outer(flag):
    def timer(func):
        def inner(*args,**kwargs):
            if flag:
                print('''执行函数之前要做的''')
            re = func(*args,**kwargs)
            if flag:
                print('''执行函数之后要做的''')
            return re
        return inner
    return timer

@outer(False)
def func():
    print(111)

func()

4.9.7 多个装饰器修饰一个函数

def wrapper1(func):
    def inner():
        print('wrapper1 ,before func')
        func()
        print('wrapper1 ,after func')
    return inner

def wrapper2(func):
    def inner():
        print('wrapper2 ,before func')
        func()
        print('wrapper2 ,after func')
    return inner

@wrapper2
@wrapper1
def f():
    print('in f')

f()
#输出
wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

原则:从上到下再从下到上;从外到内再从内到外
流程如下:

仅供参考,欢迎指正
原文地址:https://www.cnblogs.com/jjzz1234/p/11084572.html