• Python迭代器&生成器&装饰器


    1. 迭代器

    1.1 可迭代对象(Iterator)

    迭代器协议:某对象必须提供一个__next__()方法,执行方法要么返回迭代中的下一项,要么引起一个Stopiteration异常,以终止迭代(只能往后走,不能往前退)

    协议是一种规范,可迭代对象实现了迭代器协议,python的内部工具(如for循环、sum、min、max函数),使用迭代器协议访问对象

    可迭代对象(Iterator):实现了迭代器协议的对象(如何实现:对象内部定义了一个__iter__()方法),也就是可迭代对象内部要包含__iter__() 函数

    迭代器(Iterator):内部包含了__iter()__() ,同时也包含__next__()

    1.2 for循环的工作机制

    先通过__iter__()方法将可迭代对象转换为迭代器,再调用迭代器中的__next__()方法遍历,最后再抓取结束时的异常

    print("解析for循环的工作机制:")
    num = [1, 2, 3, 4, 5]
    for i in num:           # 工作机制:  num.__iter__()  ------>  num.__next__()
        print(i)

    1.3 可迭代对象与迭代器

    1)可迭代对象与迭代器之间的关系

    • 可迭代对象包含迭代器
    • 如果一个对象拥有__iter__方法,其就是可迭代对象(可以被for循环迭代);如果一个对象拥有next方法,其是迭代器
    • 定义可迭代对象,必须事先__iter__方法;定义迭代器,必须实现__iter__和next方法。

    2)迭代器的特点

    • 节省内存
    • 惰性机制
    • 不能反复,只能向下执行

    3)isinstance()判断对象是否为Iterable对象:

    from collections import Iterable
    print(isinstance([], Iterable))

    1.4 迭代器协议的使用示例

    print("使用while循环遍历一个列表:")
    index = 0
    while index < len(num):
        print(num[index])
        index += 1
    
    print("利用迭代器协议遍历一个列表:")
    iter = num.__iter__()
    print(iter.__next__())
    print(iter.__next__())
    print(iter.__next__())
    
    print("解析文件操作中对于文件内容的遍历:")
    f = open("test.txt", "r")
    f_iter = f.__iter__()      # 这里先将整个文件转换为一个迭代器,之后对迭代器调用__next__()方法,只在有需要的时候才加载文件一行内容
    # 当取出一行内容时,因为没有赋值给任何变量,所以占用的内存会被python的自动回收机制回收,所以这种遍历文件的方式只会动态的占用一小块内存
    print(f_iter.__next__(), end="")
    print(f_iter.__next__(), end="")
    print(f_iter.__next__(), end="")
    print(f_iter.__next__(), end="")
    print(f_iter.__next__(), end="")
    f.close()
    
    print("next()方法:")     # next()方法--->调用__next__()方法
    the_num = [1, 2, 3]
    the_iter = the_num.__iter__()   # 也可以直接使用iter(the_num)方法
    print(next(the_iter))     # next(the_iter)  ----->  调用 the_iter.__next__()

    2. 生成器

    2.1 生成器概述(generator)

    生成器(generator)就是可迭代对象(它在内部实现了迭代器协议)

    生成器在python中的两种表达形式:

    • 生成器表达式
    • 生成器函数

    触发生成器的方式:

    • 通过调用__next__()方法,相当于send(None)
    • 通过调用send()方法

    2.2 生成器函数

    • 只要函数中包含yield关键字,则此函数就是一个生成器函数
    • 每调用一次 __next__(),yield后面的值就会被返回一次,且保留此次的状态,下一次调用从此位置开始

    1)生成器函数

    def func():
        print("现在开始执行生成器函数:")
        print("First----->")
        yield "第一步"
        print("Second----->")
        yield "第二步"
        print("Third")
        yield "第三步"
        print("End")
        yield "生成器函数调用完毕"
    
    f = func()
    print(f.__next__())    # __next__()函数调用接受到的返回值就是yield后面的值
    print(f.__next__())
    print(f.__next__())
    print(f.__next__())

    2)用yield方式的生成器实现字典中文件的内容查找

    def get_num():
        f = open("population.txt", "r", encoding="utf-8")  # 这个文件中的每一行都是一个字典
        for i in f:
            yield i                                        # 这里返回的 i 为字符串类型
    
    population = eval(get_num().__next__())                # 这里通过eval()函数重新恢复了文件中数据的数据类型,恢复成了字典
    print("%s的人口数为%s" % (population["city"], population["population"]))
    # 注意:生成器只能遍历一次
    
    # 用生成器表达式的方式实现字典中文件数字的求和
    f = open("population.txt", "r", encoding="utf-8")
    p = sum(eval(i)["population"] for i in f)
    print(p)

    3)send()方法的使用

    def my_func():
        print("First")
        send1 = yield 1     # yield后面的值是next方法调用的返回值,而前面的值是yield接收值之后赋值的变量
        print("Second")
        print("此次send传送的值为:", send1)
        send2 = yield 2
        print("Third")
        print("此次send传送的值为:", send2)
        yield 3
        print("End")
      
    m = my_func()           # 这里并不会执行这个函数,因为只要函数内部定义了yield关键字,函数就变成了生成器,只有通过next方法调用
    print(m.__next__())
    
    print(m.send(None) )    # 这里的send(None),就相当于__next__()方法,触发一次生成器
    # send()函数将值传递给当前停留位置的yield,之后这个yield会将值传递给前面的变量
    print(m.send("这是第二次send"))

    4)yield from

    • yield from可以直接把迭代对象中的每一个数据作为生成器的结果进行返回
    • 因为yield from是将列表中的每一个元素返回,所以写两个yield from并不会产生交替的效果
    def func():
        lst1 = ['卫龙','老冰棍','北冰洋','牛羊配']
        lst2 = ['馒头','花卷','豆包','大饼']
        yield from lst1
        yield from lst2
    g = func()
    for i in g:
        print(i)
        
    # 卫龙
    # 老冰棍
    # 北冰洋
    # 牛羊配
    # 馒头
    # 花卷
    # 豆包
    # 大饼

    5)额外

    def func3():
        for i in range(10):
            yield i
    
    t = func3()
    # for i in t:            # 因为for循环的工作机制就是使用迭代器协议调用next方法,所以会触发生成器函数
    #     print(i)
    
    li1 = (i for i in t)     # 这里li1会得到一个生成器的地址
    li2 = (i for i in li1)   
    print(type(li1))
    print(list(li1))         # 这里的list函数中会用for循环机制取值,从而触发生成器,现在这个位置li1中的值已经被取完了
    print(list(li2))         # 因li1中的值已经被取完了,所以li2现在已经取不到值了

    2.3 推导式

    • 推导式有:列表推导式、字典推导式、集合推导式
    • 没有元组推导式

    1)列表推导式

    • 三元表达式
      name = "hgzero"
      ret = "me" if name == "hgzero" else "other"    # 若name等于hgzero,则返回me,否则返回other
      print(ret)
    • 列表推导式(列表解析)
      list1 = [i for i in range(10)]                 # 列表解析会将生成的列表直接放于内存中,会占用较大的内存
      print(list1)
      list2 = ["数字%d" % i for i in range(10)]       # for循环处理后将i依次往前面传递
      print(list2)
    • 筛选模式(三元表达式和列表解析的使用示例)
      print("三元表达式和列表解析结合")
      list3 = ["数字%d" % i for i in range(10) if i > 5]   # 这里不能使用else,因为已经构成了三元表达式了
      print(list3)

    2)生成器推导式

    • 将列表推导式外围的中括号换成小括号就变成了一个生成器推导式
    • 生成器推导式也可以使用筛选模式
    print("生成器表达式:")
    list2 = ("数字%d" % i for i in range(10))    # 将列表解析表达式外围的中括号换成小括号就变成了一个生成器
    # 生成器表达式更节省内存
    print(list2)
    print(list2.__next__())
    print(list2.__next__())
    print(list2.__next__())
    print(list2.__next__())
    
    # 可以将生成器表达式传递给sum()等函数,以节省占用内存的大小
    print(sum(i for i in range(100000)))        
    # 这里的sum函数里面的生成器表达式不需要加括号
    # 如果直接在sum中传入一个非常大的列表,会造成占用太多内存而导致机器卡死,而用生成器的方式传入则会动态的占用很小的一片内存

    3)字典推导式

    lst1 = ['jay','jj','huazai']
    lst2 = ['周杰伦','林俊杰','刘德华']
    dic = {lst1[i]:lst2[i] for i in range(len(lst1))}
    print(dic)

    4)集合推导式

    • 集合推导式可以直接生成一个集合
    • 集合的特点:无需、不重复,所以集合推导式自带去重功能
    lst = [1,2,3,-1,-3,-7,9]
    s = {abs(i) for i in lst}
    print(s)

    2.4 生产者消费者模型

    import time
    
    # def producer():
    #     ret = []
    #     for i in range(100):
    #         time.sleep(0.1)
    #         ret.append("包子%s" % i)
    #     return ret
    #
    # def consumer(ret):
    #     for index, baozi in enumerate(ret):
    #         time.sleep(0.1)
    #         print("第%d个人来了,吃了第%s个包子" % (index, baozi))
    #
    # p = producer()
    # consumer(p)
    
    def producer():
         c1 = consumer("hgzero")     
         c2 = consumer("wuzhihao")
         c1.__next__()
         c2.__next__()
         for i in range(10):
             time.sleep(1)
             print("现在包子 %s 已经做好了!" % i)
             c1.send("第%s个包子" % i)
             c2.send("第%s个包子" % i)
    
    def consumer(name):
        print("吃包子开始!")
        while True:
            baozi = yield
            time.sleep(1)
            print("我是 %s , 现在开始吃 %s 包子" % (name, baozi))
    
    producer()

    2.5 闭包

    在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用,这样就构成了一个闭包。简单来说,闭包就是内层函数,对外层函数变量的引用。

    一般情况下,如果一个函数执行结束,则这个函数的内部的变量以及局部命名空间中的内容都会被释放掉。但是在闭包中,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。也就说,使用闭包,可以保证外部函数中的变量在内存中常驻,以供后面的程序使用。

    其实闭包中外部函数的返回值定义成内部函数的地址,是为了防止内部函数被垃圾回收机制回收。

    def func1():  
        def func2(): 
            s = '地主家的傻儿子'
            def func3():       
                print(s)      
            return func3 
        return func2
    func1()()()

    2.6 解压序列

    # 解压序列:
    a, *b, c = [1, 2, 3, 4, 5, 6, 7, 8, 9]    # 这里的 * 号表示所有的意思
    print(a)
    print(b)
    print(c)
    
    # 交换两个变量的值:
    a = 1
    b = 2
    print("通过中间变量的形式交换:")
    c = a
    a = b
    b = c
    print(a, b)
    
    print("另外两种不引入额外变量实现交换的方式:")
    f1 = 1
    f2 = 2
    f1, f2 = f2, f1
    print(f1, f2)
    
    n1 = 1
    n2 = 2
    n1 = n1 + n2
    n2 = n1 - n2
    n1 = n1 - n2
    print(n1, n2)

    3. 装饰器

    3.1 装饰器概述(Decorators)

    • 装饰器:本质上就是函数,功能是为其他函数添加附加功能
    • 原则:
      • 不修改被修饰函数的源代码
      • 不修改被修饰函数的调用方法
    • 装饰器 = 高阶函数 + 函数嵌套 + 闭包

    3.2 装饰器的实现方式

    1)用高阶函数的方式实现装饰器的功能:不合格,因为这种方式会使工作函数调用了两次

    #    1.不修改被修饰函数的源代码
    #    2. 不修改被修饰函数的调用方式
    
    # 装饰器 = 高阶函数+函数嵌套+闭包
    import time
    
    # 用高阶函数的形式想实现装饰器的功能,不合格,因为这种方式会使工作函数调用了两次
    def work_func():
        time.sleep(1)
        print("这里是work_func函数")
    
    def dec_func(func):
        start_time = time.time()
        func()
        finish_time = time.time()
        print("这个函数运行的时间是%d" % (finish_time-start_time))
        return func
    
    work_func = dec_func(work_func)   # 这里在赋值之前就已经执行了dec_func(work_func)函数
    work_func()                   # 这里又执行了一便work_func函数

    2)用高阶函数 + 函数嵌套 + 闭包的方式实现装饰器

    import time
    def work_func2():
        time.sleep(1)
        print("这里是work_func2函数")
    
    def dec_func2(func):
        def wapper():
            start_time = time.time()
            func()
            finish_time = time.time()
            print("这个函数运行的时间是%d" % (finish_time - start_time))
        return wapper
    
    work_func2 = dec_func2(work_func2)   # 这里执行dec_func2函数时遭遇一个闭包,得到的是闭包wrapper函数的内存地址(并没有执行它)
    work_func2()                         # 这里执行上面得到的闭包函数,即执行wapper()函数

    3)用“语法糖”的形式实现装饰器

    import time
    def dec_func3(func):
        def wapper():
            start_time = time.time()
            func()
            finish_time = time.time()
            print("这个函数运行的时间是%d" % (finish_time - start_time))
        return wapper
    
    @dec_func3     # 用“语法糖”的形式实现装饰器 ,相当于 work_func3 = dec_func3(work_func3)
    def work_func3():
        time.sleep(1)
        print("这里是work_func3函数")
    work_func3()   
    
    
    def dec_func4(func):   # 装饰器改进,添加返回值和多参数功能
        def wapper(*args, **kwargs):
            start_time = time.time()
            res = func(*args, **kwargs)  # func()函数的返回值就是work_func函数中的返回值,用res接收之后,再返回出去
            finish_time = time.time()
            print("这个函数运行的时间是%d" % (finish_time - start_time))
            return res
        return wapper
    
    @dec_func4     # work_func4 = dec_func3(work_func3) 这时执行work_func4函数就是执行wapper函数,因为dec_func3函数返回的就是wapper的地址
    def work_func4(name, age):
        time.sleep(1)
        print("这里是work_func4函数")
        print("Name:【%s】,Age:【%d】" % (name, age))
        return 12345                      # 这时,work_func函数的返回值就是dec_func函数的闭包---> wapper函数的返回值
    work_func4("hgzero", 21)

    3.3 带认证功能的装饰器

    import time
    
    user = [
        {"username": "hgzero", "passwd": "123"},
        {"username": "wzh", "passwd": "123"},
        {"username": "hg", "passwd": "123"},
        {"username": "zero", "passwd": "123"},
        {"username": "abc", "passwd": "123"}
    ]
    
    login_dict = {"username": None, "login": False}    # 这里用这个字典模拟session的功能
    
    def login_func(func):
        def wrapper(*args, **kwargs):
            if login_dict["username"] and login_dict["login"]:
                ret = func(*args, **kwargs)
                return ret
            while True:
                username = input("请输入用户名:").strip()
                passwd = input("请输入密码:").strip()
                for dict in user:
                    if dict["username"] == username and dict["passwd"] == passwd:
                        login_dict["username"] = username
                        login_dict["login"] = True
                        ret = func(*args, **kwargs)
                        return ret
                else:
                    print("用户名或密码错误!")
                    time.sleep(1)
                    print("请重新输入----->")
        return wrapper
    
    @login_func
    def index():
        print("欢迎来到zero的网站主页!")
    
    @login_func
    def home(name):
        print("【%s】,欢迎来到个人中心!" % (name))
    
    @login_func
    def shopping(name):
        print("【%s】,现在您的购物车中有 %s , %s , %s " % (name, "牛奶", "香蕉", "芒果"))
    
    index()
    home("python工程师")
    shopping("python工程师")

    3.4 带参数的装饰器

    import time
    user = [
        {"username": "hgzero", "passwd": "123"},
        {"username": "wzh", "passwd": "123"},
        {"username": "hg", "passwd": "123"},
        {"username": "zero", "passwd": "123"},
        {"username": "abc", "passwd": "123"}
    ]
    
    login_dict = {"username": None, "login": False}    # 这里用这个字典模拟session的功能
    
    def func(db_type):
        def login_func(func):
            print("这个数据库的类型是:", db_type)
            def wrapper(*args, **kwargs):
                if login_dict["username"] and login_dict["login"]:
                    ret = func(*args, **kwargs)
                    return ret
                while True:
                    username = input("请输入用户名:").strip()
                    passwd = input("请输入密码:").strip()
                    for dict in user:
                        if dict["username"] == username and dict["passwd"] == passwd:
                            login_dict["username"] = username
                            login_dict["login"] = True
                            ret = func(*args, **kwargs)
                            return ret
                    else:
                        print("用户名或密码错误!")
                        time.sleep(1)
                        print("请重新输入----->")
            return wrapper
        return login_func
    
    
    @func("MySQL")  #  这里先执行func("MySQL")  --->   index = login_func(index)
    # 这里因为后面加了括号,所以是执行了func函数,执行了func函数所得到的返回值就是login_func函数的地址
    # func()函数执行得到的地址就是login_func,所以这里就等价于是 @login_func,同时也将参数传进去了,巧妙的运用了闭包的原理
    def index():
        print("欢迎来到zero的网站主页!")
    
    @func("Mongodb")
    def home(name):
        print("【%s】,欢迎来到个人中心!" % (name))
    
    @func("Redis")
    def shopping(name):
        print("【%s】,现在您的购物车中有 %s , %s , %s " % (name, "牛奶", "香蕉", "芒果"))
    
    index()
    home("python工程师")
    shopping("python工程师")

  • 相关阅读:
    企业使用数据库的12种姿势
    回归架构本质,重新理解微服务
    Java中随机数的产生方式与原理
    自动类型转换、强制类型转换、作用域、整型表数范围
    创建自定义类的对象数组
    CentOS上安装比较习惯的代码编辑器
    ubuntu 15.04 的安装遇到的问题及其解决方法
    算法思想篇(1)————枚举算法
    初来乍到
    Eclipse中获取html jsp 标签的属性提示信息方法
  • 原文地址:https://www.cnblogs.com/hgzero/p/14117967.html
Copyright © 2020-2023  润新知