zoukankan      html  css  js  c++  java
  • 23、Python之无参装饰器

    一、前置知识

    1.1 *args, **kwargs

    def index(x,y):
        print(x,y)
    
    def wrapper(*args,**kwargs):
        index(*args,**kwargs) # 本质上相当于给wrapper传入什么,给index就是调用什么
        
    wrapper(y=222,x=111)  # 111 222
    

    1.2 名称空间与作用域

    名称空间的的"嵌套"关系是在函数定义阶段,即检测语法的时候确定的
    

    1.3 函数对象

    # 1、可以把函数当做参数传入
    # 2、可以把函数当做返回值返回
    

    python中有while与for两种循环机制,其中while循环称之为条件循环,语法如下:

    1.4 函数嵌套

    函数的嵌套定义:
    def outter(func):
    	def wrapper():
    		pass
    	return wrapper
    	
    

    1.5 闭包函数

    def outter():
        x=111
        def wrapper():
            x  
        return wrapper  # 不给wrapper传参的情况下,让参数可以在外层函数(包函数)取到值
    
    f=outter()
    
    # 传参的方式一:通过参数的形式为函数体传值
    
    def wrapper(x):
        print(1)
        print(2)
        print(3)
        x
    
    wrapper(1)
    wrapper(2)
    wrapper(3)
    
    # 传参的方式二:通过闭包的方式为函数体传值
    def outter(x):
        # x=1
        def wrapper():
            print(1)
            print(2)
            print(3)
            x
        return wrapper # return outter内的wrapper那个函数的内地址
    
    f1=outter(1)
    f2=outter(2)
    f3=outter(3)
    
    wrapper=outter(1)
    

    二、什么是装饰器

    # 1、器指的是工具,可以定义成函数
    # 2、装饰指的是为其他事物添加添加额外的东西点缀	
    

    三、为什么要用装饰器

    开放封闭原则:
    	开放:指的是对拓展功能是开放的。
    	封闭:指的是对修改源代码是封闭的。
    装饰器就是可以在不修改被装饰对象源代码以及调用方式的前提下,为被装饰对象添加额外功能的工具。有了装饰器,就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。
    

    四、装饰器的实现

    4.1 无参装饰器的实现

    如果想为下述函数添加统计其执行时间的功能

    def index(x,y,z):
        time.sleep(3)
        print('index %s %s' %(x,y,z))
    
    index(111,222,333)
    # index(111,y=222,z=333) # 不同的传参方式
    

    Step1:将统计执行时间功能加上,不改变调用方式

    import time
    
    def index(x,y,z):
        start=time.time()
        time.sleep(3)
        print('index %s %s' %(x,y,z))
        stop = time.time()
        print(stop - start)
    
    index(111,222,333)  # 调用方式没变,但是改变了源代码,不符合规范
    

    Step2:遵循不修改被装饰对象源代码的原则,我们想到的解决方法可能是这样

    import time
    
    def index(x,y,z):
        time.sleep(3)
        print('index %s %s' %(x,y,z))
    
    start=time.time()
    index(111,222,333)
    stop=time.time()
    print(stop - start) 
    #虽然加上了新功能,也没修改源代码及调用方式,但是代码冗余,每次都要复制此代码块。
    

    Step3:由于上述代码冗余,所以采用了如下方案解决,但是带来了新的问题,函数的调用方式变了

    import time
    
    def index(x,y):
        time.sleep(3)
        print('index %s %s' %(x,y))
    
    def wrapper():
        start=time.time()
        index(111,222)
        stop=time.time()
        print(stop - start)
    
    wrapper()
    

    Step3:优化一,将index参数参数写活,可以为index传入任意参数

    import time
    
    def index(x,y,z):
        time.sleep(3)
        print('index %s %s %s' %(x,y,z))
    
    def wrapper(*args,**kwargs):  # 可以为index传入任意参数
        start=time.time()
        index(*args,**kwargs) # index(111,z=333,y=22)  可以为index传入任意参数
        stop=time.time()
        print(stop - start)
    
    wrapper(111,222,333)
    wrapper(111,z=333,y=222)
    

    Step3:优化二,在优化一的基础上把被装饰对象写活了,原来只能装饰index

    import time
    
    def index(x,y,z):
        time.sleep(3)
        print('index %s %s %s' %(x,y,z))
    print(index)  # <function index at 0x000001B7B1776280>
    
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    
    
    def outter(func):
        # func = index的内存地址  # wrapper内不能传入名字,所以使用闭包函数,使之在上层空间寻找到
        def wrapper(*args,**kwargs):
            start=time.time()
            func(*args,**kwargs) # index的内存地址()
            stop=time.time()
            print(stop - start)
        return wrapper  # 可以让wrapper在全局被调用到
    
    index=outter(index) # 作用:使index=wrapper的内存地址
    print(index)  # <function outter.<locals>.wrapper at 0x00000248D5922160>
    home=outter(home) # home=wrapper的内存地址
    
    index(111,y=222,z=333)  # index=wrapper的内存地址,传参后执行wrapper函数,增加了功能,不改变调用方式
    # index 111 222 333
    # 3.0008726119995117
    
    home('egon')
    # welcome egon to home page
    # 2.000373125076294
    

    Step3:优化三,将wrapper做的跟被装饰对象一模一样,以假乱真(使返回值一样)

    import time
    
    def index(x,y,z):
        time.sleep(3)
        print('index %s %s %s' %(x,y,z))
    
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    
    def outter(func):
        def wrapper(*args,**kwargs):
            start=time.time()
            res=func(*args,**kwargs)  # 赋值,变量函数的返回值
            stop=time.time()
            print(stop - start)
            return res  # 返回值,伪装成跟原函数一样
    
        return wrapper
    # 偷梁换柱:home这个名字指向的wrapper函数的内存地址
    home=outter(home)
    
    res=home('egon') # res=wrapper('egon')
    print('返回值--》',res)  # 返回值--》 None
    
    # 大方向:如何在方案三的基础上不改变函数的调用方式
    

    4.2 语法糖

    # 被装饰对象正上方的单独一行写@装饰器名字,
    # 用于解决每次使用装饰器都要运行一次函数名字与装饰器更换内存地址的操作
    
    注意:装饰器须在语法糖调用上面
    
    import time
    
    装饰器
    def timmer(func):
        def wrapper(*args,**kwargs):
            start=time.time()
            res=func(*args,**kwargs)
            stop=time.time()
            print(stop - start)
            return res
    
        return wrapper
    
    在被装饰对象正上方的单独一行写@装饰器名字
    @timmer # index=timmer(index)  不需要每次都写这个index
    def index(x,y,z):
        time.sleep(3)
        print('index %s %s %s' %(x,y,z))
    
    @timmer # home=timmer(ome)  
    def home(name):
        time.sleep(2)
        print('welcome %s to home page' %name)
    
    index(x=1,y=2,z=3)
    home('egon')
    

    4.3 无参装饰器模板

    def outter(func):
        def wrapper(*args,**kwargs):
            # 1、调用原函数
            # 2、为其增加新功能
            res=func(*args,**kwargs)
            return res
        return wrapper  # 返回wrapper,wrapper内的函数才会被调用、执行
    
  • 相关阅读:
    P1217 [USACO1.5]回文质数 Prime Palindromes
    C++ 队列(queue)堆栈(stack)实现基础
    深入理解指针—>指针函数与函数指针的区别
    C语言结构体及typedef关键字定义结构体别名和函数指针的应用
    实现常用的配置文件/初始化文件读取的一个C程序
    C语言sscanf和sprintf输入输出使用及Strlen、Memset解释
    C语言一些基础知识
    Google的开源C++单元测试框架Google Test
    网站(Web)压测工具Webbench源码分析
    web压测工具http_load原理分析
  • 原文地址:https://www.cnblogs.com/zuiyouyingde/p/12556668.html
Copyright © 2011-2022 走看看