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

    闭包函数与装饰器

    闭包函数

    什么时闭包函数?

    闭:封闭

    包:包裹

    比如手机时闭包函数(内层函数),被手机包装盒(外层函数)包裹起来,手机可以使用包装盒中的东西,内层函数可以引用外层函数的名字。

    基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。

    x = 1             #1.定义变量x=1
    def f1():         #2.定义函数f1
        def f2():     #7.定义函数f2
            print(x)  #10.打印x
        return f2     #8.返回函数f2的内存地址
    
    def f3():         #3.定义函数f3
        x = 3         #5.定义变量x=3
        f2 = f1()     #6.定义变量名f2等于函数f1执行的结果
        f2()          #9.执行f2函数,而f2函数的内容就是为打印x
    
    
    f3()              #4.执行函数f3
    #结果为
    1
    
    闭包函数定义的规范

    1 闭包函数必须在函数内部定义

    2 闭包函数可以引用外层函数的名字

    PS:闭包函数是函数嵌套 函数对象 名称空间与作用域结合体

    闭包函数是为了装饰器做准备

    也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)

    def func(y):         #1.定义函数func
         x = 100         #4.定义变量x=100
         def inner():    #5.定义函数inner
             print(x)    #7.打印X,该x即引用了外层函数定义的x=100
             print(y)    #8.打印Y
         return inner    #6.返回inner函数给外层使用
    inner = func(1)      #2.定义变量名inner等于调用传入参数y=1的func函数
    inner()              #3.执行inner函数
    #结果为
    100
    1
    

    闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。

    闭包函数的用途

    目前为止,我们得到了两种为函数体传值的方式,一种是直接将值以参数的形式传入,另外一种就是将值包给函数

    #方式一
    import requests
    def spider_func(url):
        response = requests.get(url)
        print(len(response.text))
        print(response.text)
    
    url='http://www.youdao.com/'
    spider_func(url)
    
    #方式二
    import requests
    def spider_func(url):
        def inner():
            response = requests.get(url)
            print(len(response.text))
            print(response.text)
        return inner
    #爬取有道
    inner = spider_func('http://www.youdao.com/')
    inner()
    
    

    装饰器

    什么是装饰器?

    装饰:装饰 修饰

    器:工具

    所以装饰器即装饰的工具

    PS:装饰器必须要遵循'开放封闭'原则

    开放:对函数功能的添加是开放的

    封闭:对函数功能的修改是封闭的

    装饰器的作用?

    在不修改被装饰对象源代码与调用方式的前提下,添加新的功能

    装饰器的定义必须遵循

    不修改被装饰对象源代码

    不修改被装饰对象调用方式

    为什么要使用装饰器?

    可以解决代码冗余问题,提高代码的可扩展性。

    怎么使用装饰器?

    装饰器的应用:

    统计时间和登陆认证

    编写装饰器:

    通过闭包函数来实现装饰器

    无参装饰器的实现

    #给如下函数添加统计其执行时间的功能
    import time
    def index():
        time.sleep(3)
        print('Welcome to the index page')
    
    
    index()
    
    #按照遵循不修改被装饰对象源代码的原则,正常如下
    import time
    def index():
        time.sleep(3)
        print('Welcome to the index page')
    
    
    start_time=time.time()
    index()
    stop_time=time.time()
    print('run time is %s'%(stop_time - start_time))
    #结果为
    Welcome to the index page
    run time is 3.0003936290740967
    

    考虑到还有可能要统计其他函数的执行时间,于是我们将其做成一个单独的工具,函数体需要外部传入被装饰的函数从而进行调用,我们可以使用参数的形式传入

    import time
    def index():
        time.sleep(3)
        print('Welcome to the index page')
    def wrapper(func): #通过参数接受外部的值
        start_time = time.time()
        res = func()
        stop_time = time.time()
        print(f'run time is %s' % (stop_time - start_time))
        return res
    wrapper(index)
    #输出结果为
    Welcome to the index page
    run time is 3.000837564468384
    #但是我们发现这里函数的调用方式改变了,违反了装饰器定义中不能修改被装饰对象调用方式的原则
    

    所以 我们换一种为函数体传值的方式,即将值包给函数

    import time                #1.导入时间库
    def index():               #2.定义函数index
        time.sleep(3)
        print('Welcome to the index page')
    def wait_time(func):       #3定义函数wait_time
        def wrapper():         #5.定义函数wrapper
            start_time = time.time() #8.获取开始计时的时间戳
            res = func()             #9.定义变量res等于index函数的执行结果
            stop_time = time.time()  #10.获取执行结束后的时间戳
            print(f'run time is:{stop_time - start_time}') #11.打印消耗的时间
            return res          #12.返回index函数的执行结果
        return wrapper          #6.返回wrapper函数的内存地址并将其与变量名index想对应
    index = wait_time(index)    #4.定义变量名index等于将index当作参数传入wait_time函数执行后的结果
    index()                     #7.执行index函数,即执行wrapper函数
    
    #至此我们便实现了一个无参装饰器wait_time,可以在不修改被装饰对象index源代码和调用方式的前提下为其加上新功能。但我们忽略了若被装饰的函数是一个有参函数,便会抛出异常。因为我们这里的index()是相当于调用的wrapper函数,,而函数wrapper没有参数。wrapper函数接收的参数其实是给最原始的func用的,为了能满足被装饰函数参数的所有情况,便用上*args+**kwargs组合
    
    import time                #1.导入时间库
    def index(*args, **kwargs):               #2.定义函数index
        time.sleep(3)
        print('Welcome to the index page')
    def wait_time(func):       #3定义函数wait_time
        def wrapper(*args, **kwargs):         #5.定义函数wrapper
            start_time = time.time() #8.获取开始计时的时间戳
            res = func(*args, **kwargs)             #9.定义变量res等于index函数的执行结果
            stop_time = time.time()  #10.获取执行结束后的时间戳
            print(f'run time is:{stop_time - start_time}') #11.打印消耗的时间
            return res          #12.返回index函数的执行结果
        return wrapper          #6.返回wrapper函数的内存地址并将其与变量名index想对应
    index = wait_time(index)    #4.定义变量名index等于将index当作参数传入wait_time函数执行后的结果
    index()  
    

    此时我们就可以用timer来装饰带参数或不带参数的函数了,但是为了简洁而优雅地使用装饰器,Python提供了专门的装饰器语法来取代index=timer(index)的形式,需要在被装饰对象的正上方单独一行添加@timer,当解释器解释到@timer时就会调用timer函数,且把它正下方的函数名当做实参传入,然后将返回的结果重新赋值给原函数名

    def wait_time(func):
        def wrapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs) 
            stop_time = time.time()  
            print(f'run time is:{stop_time - start_time}') 
            return res          
        return wrapper
    
    @timer # index=wait_time(index)
    def index():
        time.sleep(3)
        print('Welcome to the index page')
    index()
    
    PS:注意使用python语法糖时,装饰器一定要在被装饰对象之前定义
    
  • 相关阅读:
    数据库索引分析(一)
    对象的序列化(串行化)分析(一)
    Java 集合类 TreeSet、TreeMap
    查找杀死指定进程delphi
    delphi集合的用法
    debian 删除软件
    linux 各种国内源更新 (source)
    screen 命令使用记录
    Python 常用import
    常用Python函数
  • 原文地址:https://www.cnblogs.com/a736659557/p/11892359.html
Copyright © 2011-2022 走看看