python装饰器

Python装饰器

在说python装饰器前,我们先来讲个故事。
某天,公司主管过来找小明.
主管说:“小明啊,最近公司要新增业务,需要在原来的业务上新增加一些功能,不过好多地方用到原来的地方。你好好想下,明天给我解决方啊。”
小明不假思索地说:“好的”。
第二天,主管来到了小明的面前。主管说:"小明啊,昨天交代你的事情怎么样了?"
小明说:“没问题的。我打算直接修改那个业务的代码,然后再把引用到这部分的代码给修改下”
话还没说完,主管的脸就变红了。大吼:“你知道你这样的任务量有多大吗?公司的代码量怎么多,新业务又要急于上线,你这样要改多久和多麻烦,给你一个晚上再去思考下”å
入世未深的小明经过了一个晚上的深思熟虑。
第三天,来到了主管面前。
小明说:“主管,我想到了一个好办法,可以使用装饰器。不用修改原来的代码,可以在原来的基础上增加一些新的功能。”
主管满意的点了点头。

所以说,装饰器到底是什么东西呢?简而言之,装饰器就是修改其他函数功能的函数,他可以帮助我们的代码更加的简洁。重点:不需要修改原来函数里面的内容
直接说装饰器,可能会有点难理解和接受。在说装饰器之前,我们先来学习和理解下,什么是闭包高阶函数

高阶函数

一个函数可以作为参数传递给另外一个函数,或者一个函数的返回值为另一个函数。满足其中的一个条件就是高阶函数。

参数为函数的函数

def fun1():
    print('in the fun1......')


def fun2(fun):
    fun()
    print('in the fun2')


fun2(fun1)
# in the fun1
# in the fun2

函数的返回值为另一个函数

def fun1(num):
    print('in the fun1')
    def fun2():
        for i in  args:
            print('in the fun2')
    return fun2()


f = fun1(3)
# in the fun1
f()
# in the fun2
# in the fun2
# in the fun2

闭包

我们先来看下百度百科是怎么说的

闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁.

看完之后,发现一堆废话,压根看不懂。
简单来说,一个函数在定义的时候,引用了函数外定义的变量,并且该函数可以在其定义环境被执行,这种函数我们就叫做闭包。
先来写一个简单的闭包吧。

def counter():
    l = [0] # 函数定义外的变量,相对于fun方法而言
    
    def fun():
        l[0] += 1 # 引用函数外定义的变量
        return l[0]
    return fun

c = counter()
print(c()) #1
print(c()) #2
print(c()) #3
# c() 是fun()在其定义环境外被执行

PS

我们先来看下下面的代码

def foo():
    l = []
    for i in range(1,4):
        def bar(i):
            return i * i
        l.append(bar)
    return l


a, b, c = foo()
print(a(),b(),c()) #9,9,9 

结果是不是和你想的不一样,这就是典型的闭包陷阱
原因在于l添加函数的时候,仅仅只是添加,函数没有立即执行,等到后面i的值变为3后再执行,如果不理解再仔细看看几遍吧。
PS: 返回闭包的时候,千万不要使用循环变量或者,后续会发生改变的变量

装饰器

接下来,终于来到我们本章的重点环节了。

装饰器的本质就是一个返回函数的高阶函数。

在python中,一切都是对象。函数也是一个对象,且函数对象也是可以作为变量的,上面我们已经讨论过了。

# 我们先来定义一个累加的函数
def sum(num):
    s = 0
    for i in range(1, sum+1):
        s += i
    return s

现在我们想要增强下sum函数的功能,计算下它累加所用的时间,这时候,就是我们装饰器上场的时候了

import time


def calculate_time(func):
    a = time.time()
    def wrapper(*args, **kwargs):
        a = time.time()
        res = func(*args, **kwargs)
        b = time.time()
        print("%s  执行的时间 %s " %(func.__name__, b-a))
        return res
    return wrapper


@calculate_time
def sum(num):
    s = 0
    for i in range(1, num+1):
        s += i
    return s

print(sum(10000))
# sum执行的时间 0.0005550384521484375 
# 50005000

是不是感觉很神奇,其实这只是把我们上面学习的做了个变形而已。
把@calcuate_time放在函数的定义处,相当于执行语句
sum = calculate_time(sum)
装饰器是一个高阶函数,其返回结果是个函数,现在sum指向装饰器返回的函数,sum()执行的是返回的函数

带参数的装饰器

现在我们的要求又改变了,不应要打印出执行的时间,还要把累加的结果加上一个给定的整数。
 需求分析: 打印处执行时间。 这个我们在上面已经解决
结果再加上一个给定的整数。 这个我们可以使用带参数的装饰器

import time


def calculate_time(num):
    def decorator(func):
        a = time.time()
        def wrapper(*args, **kwargs):
            a = time.time()
            res = func(*args, **kwargs)+num
            b = time.time()
            print("%s执行的时间 %s " %( func.__name__, b-a))
            return res
        return wrapper
    return decorator


@calculate_time(555)
def sum(num):
    s = 0
    for i in range(1, num+1):
        s += i
    return s

print(sum(10000))
# sum执行的时间 0.0005428791046142578 
# 50005555

print(sum.__name__)
# wrapper

如果看不懂的话,别着急,看下下面的讲解
@calculate_time(555)加在定义函数的前面相当于sum = calculate (555)(sum)
calculate_time(555)执行函数calculatetime(555)返回decorator,接下来来到了第二层的嵌套函数,decorator(sum)返回wrapper,这时候,sum指向wrapper
 然而现在问题来了
sum对象变成了wrapper对象,sum的属性也都改变了,例如上面提到的sum.__name__;很容易想到,我们可以在编写wrapper.__name__=sum.__name__,但是,不需要,python的内置函数functools.wraps已经帮我们搞定,具体实现看下面代码

不带参数的
import time
import funtools


def calculate_time(func):
    a = time.time()
    @funtools.wrapper(func)
    def wrapper(*args, **kwargs):
        a = time.time()
        res = func(*args, **kwargs)
        b = time.time()
        print("%s  执行的时间 %s " %(func.__name__, b-a))
        return res
    return wrapper


@calculate_time
def sum(num):
    s = 0
    for i in range(1, num+1):
        s += i
    return s


print(sum.__name__) # sum
带参数的
import time


def calculate_time(num):
    def decorator(func):
        a = time.time()
        @functools.wrapper(func)
        def wrapper(*args, **kwargs):
            a = time.time()
            res = func(*args, **kwargs)+num
            b = time.time()
            print("%s执行的时间 %s " %( func.__name__, b-a))
            return res
        return wrapper
    return decorator


@calculate_time(555)
def sum(num):
    s = 0
    for i in range(1, num+1):
        s += i
    return s

print(sum(10000))
# sum执行的时间 0.0005428791046142578 
# 50005555

print(sum.__name__) # sum

完结!!!!!!!!!

posted @ 2019-11-28 15:37  You__You  阅读(127)  评论(0编辑  收藏  举报