zoukankan      html  css  js  c++  java
  • 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)))

    未完待续

  • 相关阅读:
    安装VMtools vim编辑器的使用 压缩包命令 Linux下的用户管理 (第三天)
    VM虚拟机安装 常用Linux命令 网卡配置 (第二天)
    数据库的交互模式 常用的dos命令 (第一天)
    Validate US Telephone Numbers FreeCodeCamp
    Arguments Optional FreeCodeCamp
    Everything Be True FreeCodeCamp
    Binary Agents FreeCodeCamp
    Steamroller FreeCodeCamp
    Drop it FreeCodeCamp
    Smallest Common Multiple FreeCodeCamp
  • 原文地址:https://www.cnblogs.com/fixdq/p/8670088.html
Copyright © 2011-2022 走看看