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)))

    未完待续

  • 相关阅读:
    git 仓库过大,clone不下来的解决办法
    vue项目使用elementUI pagination 实现前端分页
    Element中 Table表格数据居中显示设置
    css实现鼠标悬浮图片放大
    vue中配置开发环境、测试环境、生产环境
    vue中@keyup.enter没有作用
    LambdaToSql(轻量级ORM) 入门篇 开源项目
    04.如何升级扩展以支持Visual Studio 2019
    03. 将pdb调试文件包含到.vsix包中
    02.vs插件 获取项目和解决方案路径
  • 原文地址:https://www.cnblogs.com/fixdq/p/8670088.html
Copyright © 2011-2022 走看看