Python骚操作之函数装饰器

Python骚操作之函数装饰器

学习装饰器之前我们需要掌握的知识有函数及其作用域如果不清楚请参考和闭包的概念。在Python中函数装饰器常用于在源码中"标记"函数,以某种方式增强函数的行为(参考自《流畅的Python》一书)。

一、闭包:

**闭包:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。**下面我们来看一个例子:

def outer():
     x = 10
     def inner():#条件一:inner就是内部函数

         print(x)#条件二:x是外部环境的一个变量

     return inner#结论: 内部函数inner就是一个闭包

# inner()#局部变量,全局无法调用
#函数outer()有两种调用方式:
#第一种
# outer()()#10
#第二种
f = outer()
f()#10

二、装饰器:

概念

  装饰器本质上是一个函数,该函数用来处理其他函数,它可以让其他函数在不需要修改代码的前提下增加额外的功能。装饰器的返回值也是一个函数对象。有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

先来看一个简单例子:

def foo():
    print('I am Tom')

现在有一个新的需求,希望可以记录下函数的执行前后的时间差,于是在代码中添加日志代码:

import time.logging

def foo()
    print('Begin:',time.ctime())
    print('I am Tom')
    print('End',time.ctime())
    
foo()

如果其他函数也有类似的需求,怎么做?再在其他函数里调用时间函数?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门设定时间

def show_time(func):
        start = time.time()
        func()
        before = time.time()
        print('%s' % (before - start))
   
def foo():
    print('I am Tom')
    
show_time(foo)

逻辑上不难理解,而且运行正常。 但是这样的话,我们每次都要将一个函数作为参数传递给show_time函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,直接运行函数即可,但是现在不得不改成show_time(函数名)。那么有没有更好的方式的呢?当然有,答案就是装饰器。

简单装饰器

我们需要show_time(foo)返回一个函数对象,而这个函数对象内则是核心业务函数:func()与装饰函数:两个时间函数,修改如下:

def show_time(func):
    def inner():
        start = time.time()
        func()
        before = time.time()
        print('%s' % (before - start))
    return inner
    
def foo():
    print('I am Tom')

foo = show_time(foo)
foo()

函数show_time就是装饰器,它把真正的业务方法func包裹在函数里面,看起来像foo()被上下时间函数装饰了。

def show_time(func):
    def inner():
        start = time.time()
        func()
        before = time.time()
        print('%s' % (before - start))
    return inner

@show_time #相当于foo = show_time(foo)
def foo():
    print('foo..')
    time.sleep(2)

@show_time #相当于bar = show_time(bar)
def bar():
    print('bar..')
    time.sleep(3)

# foo = show_time(foo)
foo()#实际上是执行的inner函数!!!!

# bar = show_time(bar)
bar()

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作

如上所示,这样我们就可以省去bar = show_time(bar)这一句了,直接调用bar()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

@show_time帮我们做的事情就是当我们执行业务逻辑foo()时,实际上是执行的inner函数!!! 装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。

带参数的被装饰函数

import time

def show_time(func):
    def inner(*x,**y):
        start = time.time()
        func(*x,**y)
        before = time.time()
        print('%s' % (before - start))
    return inner

@show_time #相当于add = show_time(add)
def add(*a,**b):
    sum = 0
    for i in a:
        sum += i
    print(sum)
    time.sleep(1)

add(1,2,5,7)

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@show_time,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。

import time
def logger(flag):
    def show_time(func):
        def inner(*x,**y):
            start = time.time()
            func(*x,**y)
            before = time.time()
            print('%s' % (before - start))
            if flag == 'true':
               print('日志记录')
        return inner
    return show_time

@logger('true')#相当于@show_time
def add(*a,**b):
    sum = 0
    for i in a:
        sum += i
    print(sum)
    time.sleep(1)
add(1,2,3,4,5,6)

@logger('flase')
def bar():
    print('bar..')
    time.sleep(3)
bar()

上面的logger()是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我们使用@logger(‘true’)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

来自:http://www.cnblogs.com/yuanchenqi/articles/5694897.html

猜你喜欢

转载自blog.csdn.net/hongsong673150343/article/details/86598302