zoukankan      html  css  js  c++  java
  • 闭包函数、装饰器、迭代器

    超强台风利奇马就这样和上海擦肩而过了,今天的天气依旧艳阳高照,不幸的是我的扁桃体发炎了,又肿又痒,如万千蚂蚁在嗓子里爬动,买盒金嗓子,坚持学习。今天学了闭包函数、装饰器、迭代器,下面总结一下今天的知识点。

    一、闭包函数

    1、什么是闭包?

    • 闭包:闭是封闭(函数内部函数),包是包含(该内部函数对外部作用域而非全局作用域的变量的引用。

      闭包指的是:函数内部函数对外部作用域而非全局作用域的引用。

    • 闭包函数:传参的另外一种方式,把函数+参数包在一起返回出去

    def outter():
        x=1
        def inner():
            print(x)
        return inner
    
    f=outter()  # f接收outter的返回值inner,其中包含inner+x
    
    def f2():
        x=2
        f()  # =inner(),先在inner中找x的值,没有就到outter中找,找到x=1,输出
    f2() 
    ---------------------------------------------------
    最后输出结果为1
    

    1.1 两种为函数传参的方式

    方式一:使用参数的形式

    def func(x):
        print(x)
    
    func(1)   # 把实参1传给形参x
    func(1)   # 把实参1传给形参x
    -----------------------------------------------------------
    1
    1
    
    这种方式比较麻烦,每次传参都得在func()中给定参数
    

    方式二:包给函数

    # 函数固定格式
    def outter(x):
        x=1
        def inner():
            print(x)
        return inner
    
    f=outter(1)   #得到返回值inner,其中包着inner+x=1
    # 如果f=outter(2),输出还是1,因为查找顺序从print(x)当前位置开始,再到局部outter中x=1,不会再去找到传参的参数
    f()   # =inner(),其中x=1
    f()
    f()
    -----------------------------------------------------------
    1
    1
    1
    
    
    

    2、闭包函数的应用

    • 闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得该函数无论在何处调用,优先使用自己外层包裹的作用域。
    • 应用领域:延迟计算(原来我们是传参,现在是把函数和变量一起包起来)、爬虫领域。

    例如要定义一个爬起网站的闭包函数:

    def outter(url):
        def get_res():
            res=requests.get(url)
            print(res.text)
        return get_res
    
    baidu_spider=outter('https://www.baidu.com')  # baidu_spider=ger_res+url='https://www.baidu.com',直接就把'https://www.baidu.com'传给了url
    baidu_spider()  # =get_res(),其中url='https://www.baidu.com'
    
    #  这种想改网址,可以直接在调用阶段改实参
    taobao_spider=outter('https://www.taobao.com')
    taobao_spider()
    

    二、装饰器

    1、什么是装饰器

    器指的是工具,而程序中的函数就是具备某一功能的工具,所以装饰器指的是为被装饰函数添加额外功能。装饰器本质就是一个函数A,被装饰的对象也是一个函数B,用函数A去装饰函数B。

    注意:

    • 装饰器本身其实是可以任意可调用的对象
    • 被装饰的对象也可以是任意可调用的对象

    2、装饰器的实现的两大原则

    1、不修改被装饰对象的源代码

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

    3、装饰器模板

    应用举例:打印程序运行时间

    def deco(func):
        """装饰器函数"""
        def f1():
            start=time.time()
            func()         
            end=time.time()
            print(end-start)
        return f1
    
    import time
    def index():
        """被装饰函数"""
        print('hello index')
        time.sleep(1)
        
    index=deco(index)  # 把index传给deco的形参func,这里的前一个index=f1(),其中func=index(被装饰的函数名index)
    index()
    

    装饰器模板:

    def deco(func):
        def wrapper(*args,**kwargs):
            # 这里加要添加的功能
            res=func(*args,**kwargs)
            return res
        return wrapper
    @deco
    def shopping():
        print('shopping')
    

    4、装饰器语法糖

    在被装饰函数正上方,并且是单独一行写上@装饰器名

    def deco(func):
        """装饰器函数"""
        def f1():
            start=time.time()
            func()         
            end=time.time()
            print(end-start)
        return f1
    
    @deco  # 语法糖(更精简的代码),代替index=deco(index),可以用@deco()给deco传参
    import time
    def index():
        """被装饰函数"""
        print('hello index')
        time.sleep(1)
        
    # index=deco(index) 
    index()
    

    5、三层装饰器

    5.1 闭包包三层的运用

    def f1(y):
    
        def f2():
            x = 1
    
            def f3():
                print(f"x: {x}")
                print(f"y: {y}")
            return f3
        return f2
    
    f2 = f1(2)
    f3 = f2()
    f3()
    x: 1
    y: 2
    

    5.2 三层装饰器:给双层装饰器加参数

    5.3 三层装饰器模板

    def sanceng(engine):
        def outter(func):
            def wrapper(*args, **kwargs):  # wrapper是未来要运行的函数
                # 加功能
                print(engine)
                res = func(*args, **kwargs)  # func是被装饰的函数
                return res
            return wrapper
        return outter
    
    @sanceng('file')
    def shopping():
        print('shopping')
    

    例如:判断账号密码来自于哪个地方

    def auth(engine):
        def login(func):
            def inner(*args,**kwargs):
                # 给购物函数添加登录功能
                if engine=='file':
                    username=input('username:')
                    pwd=input('pwd:')
                    if username=='zyl'and pwd=='123':
                        print('登录成功')
                        res=func(*args,**kwargs)
                        return res
                    else:
                        print('登录失败')
                elif engine=='db':
                    print('账号密码来自于数据库,非法请求')
             return inner
        return login
    
    @ auth('file')
    def shopping():
        # 被装饰函数
        print('shopping')
        
    shopping()
                       
    

    三、迭代器

    迭代器:迭代的工具。更新换代、重复,基于上一次的结果推出下一次的结果

    3.1 可迭代对象

    定义:具有__iter__方法的对象就是可迭代对象,除了数字类型和函数类型

    可迭代对象无论迭代多少次,他的内存地址是不变的

    注意:tuple(1)与tuple(1,)类型有区别

    # 可迭代(具有__iter__方法)   对象
    
    x = 1  # 不可迭代对象
    
    s = 'nick'  # 可迭代对象
    s.__iter__()
    lt = [1, 2, 3]  # 可迭代对象
    dic = {'a': 1, 'b': 2}  # 可迭代对象
    tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象
    se = {1, 2, 3}  # 可迭代对象
    f = open('time.py')  # 可迭代对象
    
    def func():  # 不可迭代对象
        pass
    
    # 有__iter__()方法的对象就是可迭代对象,然后除了数字类型和函数之外都是可迭代对象
     s = 'nick'
     s_iter = s.__iter__()
     print(s_iter.__next__())  # 基于索引(基于上一次结果)通过__next__进行迭代
     print(s_iter.__next__())
     print(s_iter.__next__())
        
    

    3.2 迭代器对象

    只有字符串和列表都是依赖索引取值的,而其他的可迭代对象都是无法依赖索引取值的。因此我们得找到一个方法能让其他的可迭代对象不依赖索引取值。

    在找到该方法前,首先我们给出迭代器对象的概念:可迭代的对象执行__iter__方法得到的返回值。并且可迭代对象会有一个__next__方法。

    # 不依赖索引的数据类型迭代取值
    dic = {'a': 1, 'b': 2, 'c': 3}
    iter_dic = dic.__iter__()
    print(iter_dic.__next__())
    print(iter_dic.__next__())
    print(iter_dic.__next__())
    # print(iter_dic.__next__())  # StopIteration:
    ----------------------------------------------------------------
    a
    b
    c
    
    # 依赖索引的数据类型迭代取值
    lis = [1, 2, 3]
    iter_lis = lis.__iter__()
    print(iter_lis.__next__())
    print(iter_lis.__next__())
    print(iter_lis.__next__())
    # print(iter_lis.__next__())  # StopIteration:
    ----------------------------------------------------------------
    1
    2
    3
    

    上述的方法是非常繁琐的,我们可以使用while循环精简下。其中使用的try...except...为异常处理模块

    s = 'hello'
    iter_s = s.__iter__()
    
    while True:
        try:
            print(iter_s.__next__())
        except StopIteration:
            break
    ----------------------------------------------------------------
    h
    e
    l
    l
    o
    
    # 迭代器对象: 具有__iter__以及__next__方法的叫做迭代器对象
    s = 'nick'  # 可迭代对象,不属于迭代器对象
    s.__iter__()
    lt = [1, 2, 3]  # 可迭代对象,不属于迭代器对象
    dic = {'a': 1, 'b': 2}  # 可迭代对象,不属于迭代器对象
    tup = (1,)  # 元组只有一个元素必须得加逗号# 可迭代对象,不属于迭代器对象
    se = {1, 2, 3}  # 可迭代对象,不属于迭代器对象
    f = open('time.py')  # 可迭代对象,迭代器对象
    
    # 只有文件是迭代器对象
    

    总结:

    迭代器对象:执行可迭代对象的__iter__方法,拿到的返回值就是迭代器对象。

    特点:

    1. 内置__next__方法,执行该方法会拿到迭代器对象中的一个值
    2. 内置有__iter__方法,执行该方法会拿到迭代器本身
    3. 文件本身就是迭代器对象。

    缺点:

    1. 取值麻烦,只能一个一个取,并且只能往后取,值取了就没了
    2. 无法使用len()方法获取长度

    可迭代对象: 具有__iter__方法的对象就是可迭代对象,除了数字类型和函数都是可迭代对象

    迭代器对象: 具有__iter____next__方法的都是迭代器对象,只有文件

    迭代器对象一定是可迭代对象; 可迭代对象不一定是迭代器对象

    四、for循环原理

    for循环称为迭代循环,in后必须是可迭代的对象。

    lt = [1,2,3]
    lt_iter = lt.__iter__()  # 将它变为迭代器对象
    while 1:
        try:
            print(lt_iter.__next__())
        except StopIteration:    # 对他进行异常捕捉
            break
    使用for循环
    
    for i in lt:   # 可迭代对象;迭代器对象  # 不依赖索引取值,而是迭代取值
        print(i)
     '''
     1. 把lt(可迭代对象/迭代器对象)用__iter__方法转换成迭代器对象
     2. 使用__next__取出迭代器里的所有值
     3. 使用__next__方法取尽迭代器中的所有值,一定会报错,通过异常捕捉退出while循环
     '''
    
  • 相关阅读:
    PHP操作redis
    鼠标失去焦点处理办法
    关于HTTP协议,一篇就够了
    什么是 CDN(超形象)
    网站部署之~阿里云系列汇总
    阿里云系列——6.给你的域名使用CDN加速(详细步骤+简单配置)
    mysql备份与还原
    .NET框架之“小马过河”
    .NET使用Bogus生成大量随机数据
    .NET中的值类型与引用类型
  • 原文地址:https://www.cnblogs.com/zhuangyl23/p/11341474.html
Copyright © 2011-2022 走看看