Python 全栈开发:python函数装饰器

装饰器

一、为何要用装饰器

开闭原则(OCP)是面向对象中“可复用设计”的基石,是面向对象设计中最重要的原则之一,其它很多的设计原则都是实现开闭原则的一种手段。对于扩展是开放的,对于修改是关闭的,这意味着模块的行为是可以扩展的。当应用的需求改变时,我们可以对模块进行扩展,使其具有满足那些改变的新行为。也就是说,我们可以改变模块的功能。对模块行为进行扩展时,不必改动模块的源代码或者二进制代码。

 

在python中就是遵循这样的开闭原则

  原则: 1.不修改源代码

     2.不修改调用方式

  目的:在满足1,2原则的基础上,实现功能的扩展

二、什么是装饰器

装饰器是闭包函数的一种应用场景

器指的工具,装饰指的是为被装饰对象添加新功能

完整含义:
        装饰器即在不修改被装饰对象源代码与调用方式的前提下,为被装饰器对象添加新功能
        装饰器与被装饰的对象均可以是任意可调用的对象


        装饰器=》函数
        被装饰的对象=》函数

三、装饰器使用

无参装饰器

下面是一个基本的装饰器:计算每个函数执行的时间

import time
# 计算每个函数执行的时间
def warpper(func):
    def inner():
        # 函数开始的时间点
        start_time = time.time()
        func()       # 被装饰的函数
        # 函数结束的时间点
        end_time = time.time()
        # 显示执行时间
        total = end_time - start_time  # 单位秒
        print(total)
    return inner

# 语法糖
@warpper
def run():
    time.sleep(1)
    print('刚才在跑步')
    
# 调用函数
run()

 

对于这个装饰器是怎么来的下面一步步的来讲解

需要实现的功能:计算每个函数执行的时间

原则:不修改源码,不改变调用方式,添加计算时间功能

#1  首先不考虑任何东西,实现计算一个函数执行时间

def run():
        # 函数开始的时间点
        start_time = time.time()
        time.sleep(1)
        print('刚才在跑步')
        # 函数结束的时间点
        end_time = time.time()
        # 显示执行时间
        total = end_time - start_time  # 单位秒
        print(total)
# 调用
run()

功能实现:实现了一个函数的执行时间计算

源代码:被修改

调用方式:没改变

问题:如果有很多地方调用,程序猿就爆炸了。。。。

 

#2 不让修改源代码,就不改呗

def run():
        time.sleep(1)
        print('刚才在跑步')

# 函数开始的时间点
start_time = time.time()
# 调用
run()
# 函数结束的时间点
end_time = time.time()
# 显示执行时间
 # 单位秒
print(end_time - start_time)

功能实现:每个函数都要加上这三行代码才能实现功能。。。

源代码:没修改

调用方式:没改变    

问题:一个调用改一次,一万个调用,还是炸了。。。

 

#3 接着改,把函数对象作为参数

def run():
        time.sleep(1)
        print('刚才在跑步')

def timer(func):
    start_time = time.time()
    func()
    # 函数结束的时间点
    end_time = time.time()
    # 显示执行时间
    total = end_time - end_time  # 单位秒
    print(end_time - start_time)
# 调用
timer(run)   # 修改了原函数的调用方式

功能实现:可以实现

源代码:没修改

调用方式:改变    

问题:一个调用改一次,一万个调用,还是炸了。。。

 

#4 只能用闭包函数

# 闭包函数 
def wrapper(func):
    def inner():
     start_time = time.time() func()
# 原函数
     end_time = time.time()
     print(end_time - start_time) return inner
run
= wrapper(run)  #新的run=inner run()  #inner()

功能实现:实现

源代码:没修改

调用方式:没改变    

问题:run = wrapper(run) 这行代码还是要每个地方就要修改

#5 对于 #4中的问题,python给我们提供了优美的解决方案

@修饰符(语法糖)

修饰符必须出现在函数定义前一行,不允许和函数定义在同一行

一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

import time
# 计算每个函数执行的时间
def warpper(func):
    def inner():
        # 函数开始的时间点
        start_time = time.time()
        func()       # 被装饰的函数
        # 函数结束的时间点
        end_time = time.time()
        # 显示执行时间
        total = end_time - start_time  # 单位秒
        print(total)
    return inner

# 语法糖         # 装饰器必须在 语法糖前面就要定义好 ,否则找不到装饰器报错
@warpper
def run():
    time.sleep(1)
    print('刚才在跑步')
    
# 调用函数
run()

功能实现:实现

源代码:没修改

调用方式:没改变    

问题:完美没问题,计算哪个函数的执行时间就给哪个函数加个‘糖’,无需关心哪里调用

无参装饰器升级版(原函数可以任意形参,实现 return )

import time
# 计算每个函数执行的时间
def warpper(func):
    def inner(*args,**kwargs):
        # 函数开始的时间点
        start_time = time.time()
        res = func(*args,**kwargs)    # 原函数有返回值
        # 函数结束的时间点
        end_time = time.time()
        # 显示执行时间
        total = start_time - end_time  # 单位秒
        print(total)
        return res #返回值
    return inner

# 语法糖
@warpper
def run(name):
    time.sleep(1)
    print('%s刚才在跑步' % name)
    return '跑步结束'

res = run('fixd')
print(res)

 

无参装饰器模板

#无参装饰器模板
def wrapper(func):
    def inner(*args,**kwargs):
        res=func(*args,**kwargs)
        return res
    return inner

 

有参装饰器(带参数的装饰器)

带参数的装饰器:就是给装饰器传参

如果要写一个带参数的装饰器怎么办,那么我们就需要写一个三层的装饰器

import functools


def log(k=''):  # 这里参数定义的是一个默认参数,如果没有传入参数,默认为空,可以换成其他类型的参数
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('start')
            print('{}:{}'.format(k, func.__name__))  # 这里使用了装饰器的参数k
            r = func(*args, **kwargs)
            print('end')
            return r

        return wrapper
    return decorator


@log()  # fun1=log()(fun1) 装饰器没有使用参数
def fun1(a):
    print(a + 10)


fun1(10)


# print(fun1.__name__)       
# 上面装饰器如果没有@functools.wraps(func)一句的话,这里打印出的函数名为wrapper
@log('excute') # fun2=log('excute')(fun2) 装饰器使用给定参数 def fun2(a): print(a + 20) fun2(10)

 

 

 

四、装饰器语法

# 被装饰函数的正上方,单独一行
        @deco1
        @deco2
        @deco3
        def foo():
            pass

        foo=deco1(deco2(deco3(foo)))

 

未完待续

posted @ 2018-03-29 15:30  Fixdq  阅读(294)  评论(0编辑  收藏  举报