Python基础知识——装饰器

装饰器

闭包:

>>> def outer():
        x = 6  #外部环境的变量
        def inner():  #内部函数
            print(x)
        return inner  #inner即为闭包
>>> f = outer()
>>> f()
6

>>> def outer(a):  #外部环境的变量
        def inner():  #内部函数
            print(a)
        return inner  #闭包inner
>>> f = outer(6)
>>> f()
6

定义:如果在一个内部函数里,在对外部作用域(但不是全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)

闭包 = 函数块 + 定义函数时的环境

如上所示,inner()作为返回值,在外部作用域进行调用,而可以访问inner作用域之外的作用域(outer作用域)中的变量,这种现象就叫做闭包

实例:

import time
def func1():
    time.sleep(1)
    print('I\'m the first')

def func2():
    time.sleep(2)
    print('I\'m no the last')

def func3():
    time.sleep(1.5)
    print('Maybe I\'m the last')

def func4():
    time.sleep(3)
    print('I\'m the last')

func1()
func2()
func3()
func4()

如代码所示,有四个函数分别实现不同的功能

现如今有一个需求,要求每个函数都能把该函数的运行时间输出,该如何修改代码?

方法一:
import time
def func1():
    start = time.time()
    time.sleep(1)
    print('I\'m the first')
    end = time.time()
    print(end - start)

def func2():
    start = time.time()
    time.sleep(2)
    print('I\'m no the last')
    end = time.time()
    print(end - start)

def func3():
    start = time.time()
    time.sleep(1.5)
    print('Maybe I\'m the last')
    end = time.time()
    print(end - start)

def func4():
    start = time.time()
    time.sleep(3)
    print('I\'m the last')
    end = time.time()
    print(end - start)

func1()
func2()
func3()
func4()

如代码所示,直接把每个函数中的代码修改了,这样就可以实现所需的功能

对于实际的开发环境中,函数应满足封闭性和可扩展性,封闭性就是指函数一旦设计完成,无论是如何实现的,都不应对其内部的实现进行修改,可扩展性是指,函数应支持扩展,及支持增加功能。

方法一显然不符实际,直接对函数内部实现进行修改是不被允许的,而且如果函数量非常大的情况下,这种方式也不切实际。

方法二:
import time

def show_time(f):
    start = time.time()
    f()
    end = time.time()
    print(end - start)

def func1():   
    time.sleep(1)
    print('I\'m the first')

def func2():
    time.sleep(2)
    print('I\'m no the last')

def func3():
    time.sleep(1.5)
    print('Maybe I\'m the last')

def func4():
    time.sleep(3)
    print('I\'m the last')

show_time(func1)
show_time(func2)
show_time(func3)
show_time(func4)

根据方法一的经验,不对函数进行修改,另外由于方法一中实现时,
每个函数中的都是重复代码,所以可以用函数去改进,所以采用高阶函数对代码进行修改

方法二相对于方法一显然有了很大改进,而且通过函数很好的实现了功能

但是,美中不足的是,该方法对于函数的调用方式进行了修改,原本funct1()就可以直接调用该函数,而却需要全部改成show_time()才能调用,不利于其他开发人员进行该函数的调用

而且每次进行功能拓展时,如果都需要修改调用方式,显然不现实

方法三:
import time

def show_time(f):
    def inner():
        start = time.time()
        f()
        end = time.time()
        print(end - start)
    return inner

def func1():   
    time.sleep(1)
    print('I\'m the first')
func1 = show_time(func1)

def func2():
    time.sleep(2)
    print('I\'m no the last')
func2 = show_time(func2)

def func3():
    time.sleep(1.5)
    print('Maybe I\'m the last')
func3 = show_time(func3)

def func4():
    time.sleep(3)
    print('I\'m the last')
func4 = show_time(func4)

func1()
func2()
func3()
func4()

经过方法一和方法二的总结,方法三通过装饰器很好的实现了函数功能的拓展

装饰器的概念:

  • 如上例方法三所示的show_time函数就叫做装饰器函数
  • 装饰器本质就是一个函数,用于在不改变函数内部实现的情况下,为函数增加和拓展新的功能
  • 装饰器即通过内部实现闭包,而在外部调用内部函数实现已封装函数功能的拓展,这样的函数叫做装饰器

装饰器的另一种使用方法:

import time

def show_time(f):
    def inner():
        start = time.time()
        f()
        end = time.time()
        print(end - start)
    return inner

@show_time
def func1():   
    time.sleep(1)
    print('I\'m the first')
#func1 = show_time(func1)

@show_time
def func2():
time.sleep(2)
print('I\'m no the last')
#func2 = show_time(func2)

@show_time
def func3():
    time.sleep(1.5)
    print('Maybe I\'m the last')
#func3 = show_time(func3)

@show_time
def func4():
    time.sleep(3)
    print('I\'m the last')
#func4 = show_time(func4)

func1()
func2()
func3()
func4()

通过在未修改的函数前添加@+装饰器函数,可以替换在该函数之后的赋值语句
更加简单、方便, 而且逼格高

带参数的功能函数:

import time  #必须参数
def show_time(f):
    def inner(x, y):
        start = time.time()
        f(x, y)
        end = time.time()
        print(end - start)
    return inner
@show_time
def add(x, y):
    print(x+y)
    time.sleep(1)
add(1,2)

import time  #不定长参数
def show_time(f):
    def inner(*args):
        start = time.time()
        f(*args)
        end = time.time()
        print(end - start)
    return inner
@show_time
def add(*args):
    sum = 0
    for i in args:
        sum+=i
    print(sum)
    time.sleep(1)
add(1, 2, 3, 4, 5)

如果功能函数带参数时,只需直接修改装饰器里调用参数的对应位置即可

另外,对于用装饰器装饰不同参数类型的功能函数时,只需重新写一个参数不同的装饰器即可

带参数的装饰器:

假如同样对于之前的实例,需要再加上一个功能:

对于func1和func3实现输出日志的功能,而对于func2和func4不实现该功能

此时该如何去完成

import time

def outer(flag = 'False'):
    def show_time(f):
        def inner():
            start = time.time()
            f()
            end = time.time()
            print(end - start)
            if flag == 'True':
                print('log ... ...')
        return inner
    return show_time

@outer('True')
def func1():   
    time.sleep(1)
    print('I\'m the first')

@outer()
def func2():
    time.sleep(2)
    print('I\'m no the last')

@outer('True')
def func3():
    time.sleep(1.5)
    print('Maybe I\'m the last')

@outer()
def func4():
    time.sleep(3)
    print('I\'m the last')

func1()
func2()
func3()
func4()

如代码所示,在原本的装饰器外又嵌套了一层函数,又实现了一层闭包

外层函数的主要作用就是为装饰器引入了一个参数,使得装饰器可以进行有选择的判断

posted @ 2018-03-07 09:10  Clingyu  阅读(126)  评论(0)    收藏  举报