zoukankan      html  css  js  c++  java
  • 闭包函数与装饰器

    一.闭包函数
      闭包函数:1.函数内部定义的函数,成为内部函数
             2.该内部函数包含对外部作用域,而不是对全局作用域名字的引用
        特点:自带作用域
           延迟计算

    name='alex'
    # def func():
    #     name='egon'
    #     def bar():
    #         print(name)
    #     return bar
    # 
    # b=func()      
    # b()            #执行结果是egon,因为优先从函数内部调用,而对该函数外部作用域,则称为闭包函数
    View Code

      定义闭包函数的基本形式:

    #定义闭包函数的基本形式
    
    
    # def 外部函数名():
    #     内部函数需要的变量
    #     def 内部函数():
    #         引用外部变量
    #     return 内部函数
    
    # def deco():
    #     x=1
    #     def wrapper():
    #         print(x)
    #
    #     return wrapper
    #
    # wrapper=deco()
    #
    # print(wrapper)
    View Code

      一层闭包函数:

    # #包一层
    # def wrapper():
    #     money=1000
    #     def tell_info():
    #         print('egon have money %s' %(money))
    #     return tell_info
    #
    # tell_info=wrapper()
    #
    # def foo():
    #     money=100
    #     tell_info()
    
    # foo()
    View Code

      二层闭包函数:

    # 包两层
    #
    # def aaa():
    #     name='egon'
    #     def wrapper():
    #         money=1000
    #         def tell_info():
    #             print('egon have money %s' %(money))
    #             print('my namn is %s' %name)
    #         return tell_info
    #     return wrapper
    #
    # w=aaa()
    # tell_info=w()
    # print(tell_info.__closure__[0].cell_contents)
    # print(tell_info.__closure__[1].cell_contents)
    
    
    
    
    
    
    
    
    '''
    报错NameError: name 'money' is not defined
    
    原因:
        函数的作用域关系在函数定义阶段就已经固定,与调用位置无关
        无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系
        此例:虽然tell_info('egon')是在foo内调用并且引用money,但仍需要回到定义
        tell_info的阶段去找作用域关系,而定义时tell_info引用的money就是全局的money
        如果全局不存在则抛出异常NameError
    
    '''
    View Code

    定义闭包函数的基本形式
      def 外部函数名():
        内部函数需要的变量
        def 内部函数():
          引用外部变量
        return 内部函数

    def f1():
        name='good'
        def f2():
            print(name)
        return f2     

    二:装饰器:

    装饰器定义:本质就是函数,功能是为其他函数添加新功能<受限于知识,目前仅限于函数>

      '''
      一:软件的开放封闭原则,对扩展是开放的,对修改是封闭的,即源代码封闭(被装饰对象是不可更改的),但功能的扩展可依靠装饰器来实现。
      二:装饰器,装饰器本质可以任意可调用对象,被装饰的对象也可以是任意可调用对象
      装饰器的功能是:
        在不修改被装饰对象源代码以及被装饰对象的调用方式的前提下为其添加新功能
      原则:
        1.不修改源代码
        2.不修改调用方法     <如果装饰器只有一层,类似于函数,则调用起来就改变了调用方法,因而必须两层>

    https://www.zhihu.com/question/26930016

       目标:添加新功能
       '''

    装饰器的语法:
        在被装饰对象的正上方的单独一行,@装饰器名字

    例如:

    @timmer
    def index():
        time.sleep(rand,randrange(1,5))
        print('welcome to index page')     
    
    @timmer------>把正下方函数名index当一个参数传给timmer函数,然后运行timmer函数,并且把返回值重新绑定给index
    # import time
    # import random
    # #装饰器
    # def timmer(func):
    #     def wrapper():
    #         start_time = time.time()
    #         func()
    #         stop_time=time.time()
    #         print('run time is %s' %(stop_time-start_time))
    #     return wrapper
    # #被装饰函数
    # @timmer #index=timmer(index)
    # def index():
    #     time.sleep(random.randrange(1,5))
    #     print('welecome to index page')
    # # @timmer #home=timmer(home)
    # # def home():
    # #     time.sleep(random.randrange(1,3))
    # #     print('welecome to HOME page')
    #
    # index() #wrapper()
    # # home()
    View Code

    A.无参装饰器

      有如下实例,我们需要计算一下代码执行的时间。

    import time, random
    
    def index():
        time.sleep(random.randrange(1, 5))
        print("welcome to index page")

      根据装饰器的特点,我们不能对index()进行任何修改,而且调用方式也不能变。这时候,我们就可以使用装饰器来完成如上功能.

    import time, random
    
    
    def outer(func):  # 将index的地址传递给func
        def inner():
            start_time = time.time()
            func()   # fun = index  即func保存了外部index函数的地址
            end_time = time.time()
            print("运行时间为%s"%(end_time - start_time))
        return inner  # 返回inner的地址
    
    
    def index():
        time.sleep(random.randrange(1, 5))
        print("welcome to index page")
    
    index = outer(index)  # 这里返回的是inner的地址,并重新赋值给index
    
    index()
    View Code

      但是,有些情况,被装饰的函数需要传递参数进去,有些函数又不需要参数,那么如何来处理这种变参数函数呢?下面来看看有参数装饰器的使用情况.

    B.有参装饰器

    def outer(func):  # 将index的地址传递给func
        def inner(*args, **kwargs):
            start_time = time.time()
            func(*args, **kwargs)   # fun = index  即func保存了外部index函数的地址
            end_time = time.time()
            print("运行时间为%s"%(end_time - start_time))
        return inner  # 返回inner的地址

     那如果一个函数被多个装饰器装饰,那么执行顺序是怎样的:

    import time
    import random
    
    def timmer(func):
        def wrapper():
            start_time = time.time()
            func()
            stop_time=time.time()
            print('run time is %s' %(stop_time-start_time))
        return wrapper
    def auth(func):
        def deco():
            name=input('name: ')
            password=input('password: ')
            if name == 'egon' and password == '123':
                print('login successful')
                func() #wrapper()
            else:
                print('login err')
        return deco
    
    @auth   # index = auth(timmer(index))                 
    @timmer # index = timmer(index)
    def index():
     
        time.sleep(3)
        print('welecome to index page')
    
    
    
    index()
    View Code

    实验结果表明,多个装饰器装饰一个函数,其执行顺序是从下往上。

    Return函数返回值与函数运算结果的区别:

    def foo(x,y):
        print('你就是牛逼')
        z=x+y
        print(z)
        return None
    
    print(foo(1,2))   
    ===============结果================
    你就是牛逼
    3
    None
    
    ###将函数运算foo(1,2)进行打印得到的是函数的返回值,也就是return的值,在这里是None;而不是想当然的认为是函数运行的结果3
    View Code
  • 相关阅读:
    【Mesh R-CNN】论文翻译(原理部分)
    关于栈的学习记录
    css入门笔记
    前端学习之html基础知识归纳
    navigator url无法跳转跳转问题
    新手小白学会vim脚本配置
    Linux下实现两个变量之间传参
    [Apache Doris] Apache Doris 架构及代码目录解读
    [编程总结] 开源系统平台化建设思路
    论文阅读|PointRend: Image Segmentation as Rendering
  • 原文地址:https://www.cnblogs.com/junxiansheng/p/7008882.html
Copyright © 2011-2022 走看看