zoukankan      html  css  js  c++  java
  • 迭代器与生成器

    迭代器

    迭代器:迭代的工具。迭代可以看成重复,并且每一次的重复都是基于上次的结果来的,不是单纯的重复。

    可迭代对象

    python中一切皆对象,如:

    x = 1
    name = 'william'
    lis = [1, 2, 3, 4]
    tup = (1, 2, 3, 4)
    dic = {'name': 'william'}
    s = {'a', 'b'}
    
    
    def func():
        pass
    
    
    f = open('602.txt', 'w', encoding='utf8')
    

    对于这一切对象,只要有__iter__方法的对象,都是可迭代对象。

    # x = 1.__iter__   # SyntaxError: invalid syntax
    
    # 以下都是可迭代对象
    
    name = 'william'.__iter__
    lis = [1, 2, 3, 4].__iter__
    tup = (1, 2, 3, 4).__iter__
    dic = {'name': 'william'}.__iter__
    s = {'a', 'b'}.__iter__
    f = open('602.txt', 'w', encoding='utf8')
    f.__iter__
    f.close()
    

    总结

    可迭代的对象:Python内置的str,list,tuple,dict,set,file都是可迭代对象。

    特点:内置有__iter__方法的都叫可迭代的对象。

    迭代器对象

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

    首先我们要知道迭代器对象的概念:可迭代对象执行__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__方法,拿到的返回值就是迭代器对象。

    特点:

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

    缺点:

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

    for循环原理

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

    lis = [1, 2, 3]
    for i in lis:
        print(i)
    
    1
    2
    3
    

    因为迭代器使用__iter__后还是迭代器本身,因此for循环不用考虑in后的对象是可迭代对象还是迭代器对象。

    由于对可迭代对象使用__iter__方法后变成一个迭代器对象,这个迭代器对象只是占用一小块内存空间,只有使用__next__后才会吐出一个一个值。如lis = [1,2,3,4,5]相当于一个一个鸡蛋,而lis = [1,2,3,4,5].__iter__相当于一只老母鸡,如果你需要蛋,只需要__next__即可。

    # python2中
    print(range(10))  # [0,1,2,3,4,5,6,7,8,9]
    
    # python中
    print(range(10))  # range(0,10)
    

    三元表达式与列表推导式

    三元表达式

    '条件成立时的返回值'if'条件'else'条件不成立时的返回值'。

    x = 10
    y = 20
    
    print(f'x if x > y else y : {x if x > y  else y}')
    
    x if x > y else y : 20
    

    列表推导式

    [expression for item1 in iterable1 if condition1
     for item2 in iterable2 if condition2
     ...
     for itemN in iterableN if conditionN
     ]
    类似于
    res = []
    for item1 in iterable1:
        if condition1:
            for item2 in iterable2:
                if condition2:
                    ...
                    for itemN in iterableN:
                        if conditionN:
                            res.append(expression)
    
    print(f'[i for i in range(8)] :{[i for i in range(8)] }')
    
    [i for i in range(8)] :[0, 1, 2, 3, 4, 5, 6, 7]
    
    print(f'[i**2 for i in range(8)] : {[i**2 for i in range(8)]}')
    
    [i**2 for i in range(8)] : [0, 1, 4, 9, 16, 25, 36, 49]
    
    name_list = ['william', 'tom', 'jerry']
    
    print(
        f"[name if name == 'william' else name +'funny' for name in name_list]:{[name if name == 'william' else name + 'funny' for name in name_list] }")
    
    [name if name == 'william' else name +'funny' for name in name_list]:['william', 'tomfunny', 'jerryfunny']
    

    字典生成式

    字典生成式

    print({i: i**2 for i in range(8)})
    
    {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49}
    

    zip()方法

    keys = ['name', 'age', 'gender']
    values = ['william', 18, 'male']
    
    res = zip(keys, values)
    print(f'zip(keys,values):{zip(keys,values)}')
    
    info_dict = {k: v for k, v in res}
    print(f'info_dict :{info_dict}')
    
    zip(keys,values):<zip object at 0x00000256E9B28548>
    info_dict :{'name': 'william', 'age': 18, 'gender': 'male'}
    

    通过解压缩函数生成一个字典。

    info_dict = {'name': 'william', 'age': 18, 'gender': 'male'}
    print(f'info_dict.keys():{info_dict.keys()}')
    print(f'info_dict.values():{info_dict.values()}')
    
    res = zip(info_dict.keys(), info_dict.values())
    print(f'zip(keys,values):{zip(info_dict.keys(),info_dict.values())}')
    
    info_dict = {k: v for k, v in res}
    print(f'info_dict:{info_dict}')
    
    info_dict.keys():dict_keys(['name', 'age', 'gender'])
    info_dict.values():dict_values(['william', 18, 'male'])
    zip(keys,values):<zip object at 0x00000256E9B28988>
    info_dict:{'name': 'william', 'age': 18, 'gender': 'male'}
    

    生成器

    yield关键字

    yield意思为生产,在函数中但凡出现yield关键字,再调用函数,就不会继续执行函数体代码,而是会返回一个值。

    def func():
        print(1)
        yield
        print(2)
        yield
    
    
    g = func()
    print(g)
    
    <generator object func at 0x00000256E9B0CFC0>
    

    生成器的本质就是迭代器,同时也并不仅仅是迭代器,不过迭代器之外的用途实在是不多,所以我们可以说:生成器提供了非常方便的自定义迭代器的途径。

    def func():
        print('from func1')
        yield 'a'
        print('from func2')
        yield 'b'
    
    
    g = func()
    print(f'g.__iter__ == g :{g.__iter__() == g}')
    
    res1 = g.__next__()
    print(f'res1:{res1}')
    
    res2 = next(g)
    print(f'res2:{res2}')
    
    # next(g)  # StopIteration
    
    g.__iter__ == g :True
    from func1
    res1:a
    from func2
    res2:b
    
    def func():
        print('from func1')
        yield 'a'
        print('from func2')
        yield 'b'
    
    
    g = func()
    for i in g:
        print(i)
    
    print(f'list(func():{list(func())})')
    
    from func1
    a
    from func2
    b
    from func1
    from func2
    list(func():['a', 'b'])
    

    yield + return??

    既然生成器也是函数,那么它可以使用return输出返回值么?

    既然都选择自定义一个函数作为生成器,还return干什么,Python2中会报异常,Python中,无视这种行为。

    def i_wanna_return():
        yield 'a'
        yield 'b'
        return None
        yield 'c'
    
    
    for i in i_wanna_return():
        print(i)
    
    a
    b
    

    迭代器套迭代器

    如果我需要在生成器的迭代过程中接入另一个生成器的迭代怎么办?写成下面这样好傻好天真。

    def sub_generation():
        yield 1
        yield 2
        for i in range(3):
            yield i
    
    
    for i in sub_generation():
        print(i)
    
    1
    2
    0
    1
    2
    
    def sub_generation():
        yield 1
        yield 2
        yield from range(3)
    
    
    for i in sub_generation():
        print(i)
    
    1
    2
    0
    1
    2
    

    协同程序

    协同程序(协程)一般来说是指这样的函数:

    • 彼此间有不同的局部变量、指令指针,但仍共享全局变量;
    • 可以方便的挂起、恢复,并且有多个入口点和出口点;
    • 多个协同程序间表现为协作运行,如A的运行过程中需要B的结果才能继续执行。

    协程的特点决定了同一时刻只能有一个协同程序正在运行(忽略多线程的情况)。得益于此,协程间可以直接传递对象而不需要考虑资源锁、或是直接唤醒其他协程而不需要主动休眠,就像是内置了锁的线程。在符合协程特点的应用场景,使用协程无疑比使用线程要更方便。

    从另一方面说,协程无法并发其实也将他的应用场景限制在了一个很狭窄的范围,这个特点使得协程更多被拿来与常规函数进行比较,而不是与线程。当然,线程比协程复杂许多,功能也更强大,所以牢牢掌握线程即可。

    自定义range()方法

    def my_range(start, stop, step=1):
        while start < stop:
            yield start
            start += 1
    
    
    g = my_range(0, 3)
    print(f'list(g):{list(g)}')
    
    list(g):[0, 1, 2]
    

    总结

    yield:

    1. 提供一种自定义迭代器的方式。
    2. yield可以暂停住函数,并提供当前返回值。

    yield和return:

    • 相同点:两者都是在函数内部使用,都可以返回值,并且返回值没有类型和个数的限制
    • 不同点:return只能返回一次值,yield可以返回多次值。

    生成器表达式

    • 把列表推导式的[]换成()就是生成器表达式
    • 优点:省内存,一次只产生一个值在内存中
    t = (i for i in range(8))
    print(t)
    print(f'next(t):{next(t)}')
    
    <generator object <genexpr> at 0x00000256EABFE150>
    next(t):0
    

    生成器表达式和列表推导式

    列表推导式相当于直接给你一筐蛋,而生成器表达式相当于给你一只老母鸡。

    # 生成器表达式
    with open('602.txt', 'r', encoding='utf8') as f:
        nums = [len(line) for line in f]
    
    print(max(nums))  # len(william)
    
    7
    
    # 列表推导式
    with open('602.txt', 'r', encoding='utf8') as f:
        nums = (len(line) for line in f)
    
    print(max(nums))  # ValueError: I/O operation on closed file.
    
  • 相关阅读:
    apache http server 和tomcat的区别 以及nginx
    2020-2-12 这样提升自己的口才
    两种常用的队列
    栈的实现与应用
    线性表
    Nginx实现虚拟主机
    将apache添加到服务
    apache安装
    最小生成树
    图的深度优先搜索
  • 原文地址:https://www.cnblogs.com/WilliamKong94/p/11005371.html
Copyright © 2011-2022 走看看