Python函数-装饰器

装饰器的深入

1. 装饰器的使用法则

开放封闭原则:

  1.对扩展是开放的

    为什么要对扩展开放呢?

    我们说,任何一个程序,不可能在设计之初就已经想好了所有的功能并且未来不做任何更新和修改。所以我们必须允许代码扩展、添加新功能。

  2.对修改是封闭的

    为什么要对修改封闭呢?

    就像我们刚刚提到的,因为我们写的一个函数,很有可能已经交付给其他人使用了,如果这个时候我们对其进行了修改,很有可能影响其他已经在使用该函数的用户。

装饰器完美的遵循了这个开放封闭原则。

2. 装饰器的主要功能和装饰器的固定结构

装饰器的主要功能

在不改变函数调用方式的基础上在函数的前、后添加功能。

装饰器的固定格式

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

装饰器的固定格式wraps版--后续了解

from functools import wraps

def deco(func):
    @wraps(func) #加在最内层函数正上方
    def wrapper(*args,**kwargs):
        return func(*args,**kwargs)
    return wrapper

装饰器的固定格式——wraps版

3. 装饰器基础需求-计算一个函数的运行时间

见博文

http://www.cnblogs.com/wangph/p/8875544.html

计算一个函数的运行时间
import time
def timmer(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        ret = f(*args,**kwargs)
        end_time = time.time()
        print(end_time - start_time)
        return ret
    return inner

@timmer   # func = timmer(func)
def func(a,b):
    print('begin func',a)
    time.sleep(0.1)
    print('end func',b)
    return True

ret = func(1,2)   #--> inner()


def timmer(f):
    def inner(*args,**kwargs):

        ret = f(*args,**kwargs)

        return ret
    return inner

4. 装饰器进阶需求

4.1 确认是否启用装饰器-装饰器的参数

如何确认你的装饰器是否生效呢?最好的方式 是传参决定

我们的方法是,在原装饰器的外层,再加一层函数,然后return原装饰器的函数名。

之后在装饰器内层函数中加判断,确认参数的质量是否生效。

import time

FLAG = True
def outer(flag):   #在装饰器timmer的外层加outer函数,接收传参 flag
    def timmer(f):
        def inner(*args,**kwargs):
            if flag == True:        ##在装饰器的中的内层函数inner中判断传参,确认装饰器是否生效
                start_time = time.time()
                ret = f(*args,**kwargs)
                end_time = time.time()
                print(end_time - start_time)
            else:
                ret = f(*args, **kwargs)
            return ret
        return inner
    return timmer  # outer函数中返回装饰器的函数名timmer

@outer(FLAG)   # func = timmer(func)    #调用装饰器,调用outer即可,在后面加实参
def func(a,b):
    print('begin func',a)
    time.sleep(0.1)
    print('end func',b)
    return True

func(1,2)

4.2 一个函数使用多个装饰器来装饰

eg:一个函数需要同时使用确认登录和计算运行时间的装饰器

现在我已经有了如下两个装饰器和一个函数,那么我们如何调用这两个装饰器对着一个函数进行装饰呢?

import time
login_info = {'alex':False}
def login(func):   # manager
    def inner(name):
        if login_info[name] != True:
            user = input('user :')
            pwd = input('pwd :')
            if user == 'alex' and pwd == 'alex3714':
                login_info[name] = True
        if login_info[name] == True:
            ret = func(name)     # timmer中的inner
            return ret
    return inner

def timmer(f):
    def inner(*args,**kwargs):
        start_time = time.time()
        ret = f(*args,**kwargs)     # 调用被装饰的方法
        end_time = time.time()      #
        print(end_time - start_time)
        return ret
    return inner


def index(name):
    print('欢迎%s来到博客园首页~'%name)

使用多个装饰器的方法

使用方法一:

@timmer
@login
def index(name):
    print('欢迎%s来到博客园首页~'%name)

执行结果

user :alex
pwd :alex3714
欢迎alex来到博客园首页~
6.77495002746582

使用方法二:

@login
@timmer
def index(name):
    print('欢迎%s来到博客园首页~'%name)

执行结果呢

user :alex
pwd :alex3714
欢迎alex来到博客园首页~
3.0994415283203125e-05

我们发现两种调用装饰器的方法差异是:两个装饰器的上下顺序不相同。

返回的结果是第二张方法timmer计算了函数index的执行时间,而第一个方法timmer则计算了index的执行时间和装饰器login的运行时间

由此我们可以发现,使用装饰器的时候,装饰器对它正下方挨着的函数(装饰器)生效 !


其中的函数执行关系我们可以通过下面一段代码来了解

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

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

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

f()

让我们来看一下这段函数的执行返回结果

wrapper2 ,before func
wrapper1 ,before func
in f
wrapper1 ,after func
wrapper2 ,after func

我们来看图分析下当函数f使用了两个装饰器的时候,它的执行过程:

'''
根据上面的分析,当f调用两个装饰器的时候,首先执行的是最上面装饰器的内层函数 iunner2
这时候到了执行阶段
执行inner2 第一步 print('wrapper2 ,before func')
        第二步  func() = inner1() 
                        执行inner1的第一步 print('wrapper1 ,before func')
                                第二步 func() = f
                                        执行 f中的主函数     print('in f')
                                第三步 执行 inner1的  print('wrapper1 ,after func')
        第三步  func() = inner1()  inner()1执行完了,func()就执行完了。继续往下走 执行 print('wrapper2 ,after func')
函数执行结束

在这个过程中要注意语法糖中的变量指向和装饰器中func接收的实参变化 
                                                                                      
'''

 

猜你喜欢

转载自www.cnblogs.com/wangph/p/8932534.html