• 迭代器和生成器


    迭代器(iterator) 

    迭代器协议:必须拥有__iter__方法和__next__方法

    迭代的概念

    #迭代器即迭代的工具,那什么是迭代呢?
    #迭代是一个重复的过程,每次重复即一次迭代,并且每次迭代的结果都是下一次迭代的初始值
    while True: #只是单纯地重复,因而不是迭代
        print('===>') 
        
    L=[1,2,3]
    count=0
    while count < len(L): #迭代
        print(l[count])
        count+=1

    #dir() ----(展示所拥有的所有方法)
    print
    (dir([1,2])) print(dir((2,3))) print(dir({'a':1,'b':2})) print(dir({1,2,3,4})) ##以下所展示的有双下方法:如'__add__', '__class__'它们是Python解释器的内部方法,也称为双下方法。是由C语言写的,可能不止一种方式调用它。
    # ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] # ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index'] # ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values'] # ['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__init_subclass__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']

    ##双下方法:

    print([1].__add__([2]))
    print([1]+[2])
    ###
    [1, 2]
    [1, 2]

     由上可以看出,可以被for循环的都是可迭代的,要想可迭代,内部必须要有一个__iter__方法。

    首先,判断 int 类型有没有__iter__方法:

    print("__iter__" in dir(int))
    # False

    ##执行以下__iter__方法:

    print([1,2].__iter__())
    
    # <list_iterator object at 0x000001A68B0210B8> #生产一个迭代器

    得到了一个list_iterator,此时我们又得到了一个新的名词——iterator。(迭代器)

    同样的,可以对字典、集合、元祖进行__iter__操作

    print((1,2,3).__iter__())
    print({'a':1,'b':2}.__iter__())
    print({1,2,3}.__iter__())
    
    # <tuple_iterator object at 0x0000021688F61080>
    # <dict_keyiterator object at 0x0000021688E8A4A8>
    # <set_iterator object at 0x0000021688F60630>

    同样的得到了tuple_iterator、dict_keyiterator、set_iterator,这就说明了它们都是可迭代的。

    可以通过集合求差集来看迭代器与可迭代对象质检的差别。

    print(set(dir([1,2].__iter__()))-set(dir([1,2])))
    
    # {'__next__', '__length_hint__', '__setstate__'}
    
    
    

    迭代器:必须拥有__iter__方法和__next__方法

    print("__iter__" in dir([].__iter__()))
    print("__next__" in dir([].__iter__()))
    
    ###
    True
    True
    ############
    from
    collections import Iterable from collections import Iterator print(isinstance([],Iterator)) print(isinstance([],Iterable )) ############ False True 列表是可迭代的,但不是一个迭代器

    #####或者创造一个数据类型:

    from collections import Iterable
    from collections import Iterator
    class A:
        def __iter__(self):pass
        def __next__(self):pass
    a = A()
    print(isinstance(a,Iterator))
    print(isinstance(a,Iterable ))
    
    ###
    True
    True
    再次说明,
    迭代器:必须拥有__iter__方法和__next__方法
    PS:可迭代的.__iter__()方法就可以得到一个迭代器
      迭代器中的__next__()方法可以一个一个获取值

    ######################################补充说明###############################################

    #1、为何要有迭代器?
    对于序列类型:字符串、列表、元组,我们可以使用索引的方式迭代取出其包含的元素。但对于字典、集合、文件等类型是没有索引的,若还想取出其内部包含的元素,则必须找出一种不依赖于索引的迭代方式,这就是迭代器
    
    #2、什么是可迭代对象?
    可迭代对象指的是内置有__iter__方法的对象,即obj.__iter__,如下
    'hello'.__iter__
    (1,2,3).__iter__
    [1,2,3].__iter__
    {'a':1}.__iter__
    {'a','b'}.__iter__
    open('a.txt').__iter__
    
    #3、什么是迭代器对象?
    可迭代对象执行obj.__iter__()得到的结果就是迭代器对象
    而迭代器对象指的是即内置有__iter__又内置有__next__方法的对象
    
    文件类型是迭代器对象
    open('a.txt').__iter__()
    open('a.txt').__next__()
    
    
    #4、注意:
    迭代器对象一定是可迭代对象,而可迭代对象不一定是迭代器对象

    迭代器的作用:

    dic={'a':1,'b':2,'c':3}
    iter_dic=dic.__iter__() #得到迭代器对象,迭代器对象即有__iter__又有__next__,但是:迭代器.__iter__()得到的仍然是迭代器本身
    iter_dic.__iter__() is iter_dic #True
    
    print(iter_dic.__next__()) #等同于next(iter_dic)
    print(iter_dic.__next__()) #等同于next(iter_dic)
    print(iter_dic.__next__()) #等同于next(iter_dic)
    # print(iter_dic.__next__()) #抛出异常StopIteration,或者说结束标志
    
    #有了迭代器,我们就可以不依赖索引迭代取值了
    iter_dic=dic.__iter__()
    while 1:
        try:
            k=next(iter_dic)
            print(dic[k])
        except StopIteration:
            break
            
    #这么写太丑陋了,需要我们自己捕捉异常,控制next,python这么牛逼,能不能帮我解决呢?能,请看for循环

    for循环:(for循环其实就是在使用迭代器)

    #基于for循环,我们可以完全不再依赖索引去取值了
    dic={'a':1,'b':2,'c':3}
    for k in dic:
        print(dic[k])
    
    #for循环的工作原理
    #1:执行in后对象的dic.__iter__()方法,得到一个迭代器对象iter_dic
    #2: 执行next(iter_dic),将得到的值赋值给k,然后执行循环体代码
    #3: 重复过程2,直到捕捉到异常StopIteration,结束循环

     迭代器的优缺点:

    #优点:
      - 提供一种统一的、不依赖于索引的迭代方式 (方便使用,从容器数据类型中一个一个取值,并把所有的值都取到- 惰性计算,节省内存(迭代器并不会在内存中占用一大块内存来保存数据,而是随着循环,每次只生成一个数据)
    #缺点:
      - 无法获取长度(只有在next完毕才知道到底有几个值)
      - 一次性的,只能往后走,不能往前退

     #只有可迭代对象的时候才能用for循环

    #当遇到一个变量,不确定是否能用for循环是,先判断它是否可迭代

    #############################################################################################

    生成器

    什么是生成器(生成器的本质是迭代器)

    
    
    #只要函数内部包含有yield关键字,那么函数名()的到的结果就是生成器,并且不会执行函数内部代码
    #含有yield关键字的函数就是 生成器函数
    #调用生成器函数的时候,函数不执行,而是返回一个生成器
    #每次调研阶段next方法,就会取到一个值得
    #直达取到最后一个,再采用next方法就会报错

    def
    generator():
    print('====>first')
        yield 1
        print('====>second')
        yield 2
        print('====>third')
        yield 3
        print('====>end')
    
    g = generator()
    print(g) #<generator object generator at 0x00000299A9D16AF0>
    print(g.__next__())
    print(g.__next__())
    print(g.__next__())
    ################
    或者:
    for i in g:
      print(i)
    ##### <generator object generator at 0x00000299A9D16AF0> ====>first 1 ====>second 2 ====>third 3

     ##wahaha:

    # 哇哈哈%i
    def wahaha():
        for i in range(100):
            yield "哇哈哈%d"%i
    
    g = wahaha()
    count = 0
    for i in g:
        count += 1
        print(i)
        if count > 10:
            break
    print("****",g.__next__()) #生成器记录当前的位置以及下一步要走的位置
    ###
    哇哈哈0
    哇哈哈1
    哇哈哈2
    哇哈哈3
    哇哈哈4
    哇哈哈5
    哇哈哈6
    哇哈哈7
    哇哈哈8
    哇哈哈9
    哇哈哈10
    **** 哇哈哈11
    #监听过滤文件,文件有“Python”就打印
    def tail(filename):
        f = open(filename,encoding="utf-8")
        while 1:
            line = f.readline()
            if line.strip():
                yield line.strip()
    
    g = tail("file")
    for i in g:
        if "python" in i:
            print("$$$$$",i)

     生成器表现形式:

    1.生成器函数

    #含有yield关键字的函数就是生成器函数

    #特点:调用函数之后,函数不执行,而是返回一个生成器;每次调用next方法的时候会取到一个值;直到取到最后一个,再执行next会报错

    2.生成器表达式

    #从生成器中取值的方法:next;for;数据类型的强制转换,占内存。

    生成器进阶:

    send获取下一个值的效果和next基本一样只是在获取下一个值的时候,给上一个yield的位置传递一个数据

    使用send的注意事项

    1)、send作用范围和next一样

    2)、第一次使用生成器的时候,是用next方法获取下一个值

    3)、最后一个yield不能接受外部的值

    def generator():
        print(123)
        content=yield 1
        print("========",content)
        arg = yield 2
        ''''''
        yield 
    
    g = generator()
    ret = g.__next__()
    print("$$$$$$$$",ret)
    ret = g.send("Hello!") # send 的效果和next一样
    print("########",ret)
    ###
    123
    $$$$$$$$ 1
    ======== Hello!
    ######## 2

    ##用生成器,获取移动平均值#移动平均值

    def average():
        sum = 0
        count = 0
        avg = 0
        while 1:
            num = yield avg
            sum += num
            count += 1
            avg = sum/count
    avg_g = average()
    avg_g.__next__()
    print(avg_g.send(10))
    print(avg_g.send(20)
    print(avg_g.send(30))
    ### 
    10.0
    15.0
    20.0
    #带装饰器的移动平均值;#在调用被装饰生成器函数的时候首先用next激活生成器
    #预激生成器: def init(func): def inner(*args,**kwargs): g = func(*args,**kwargs) next(g) #g.__next__() return g return inner @init def average(): sum = 0 count = 0 avg = 0 while 1: num = yield avg sum += num count += 1 avg = sum/count avg_g = average() print(avg_g.send(10)) print(avg_g.send(20)) print(avg_g.send(30))
    ### 
    10.0
    15.0
    20.0
    #在调用被装饰生成器函数的时候首先用next激活生成器

     ###yield from

    ##
    def gen1():
    for c in 'AB':
    yield c
    for i in range(3):
    yield i
    print(list(gen1())) #数据类型的强制转换
    #['A', 'B', 0, 1, 2]
    ##
    def gen2():
    yield from 'AB'
    yield from range(3)

    print(list(gen2()))
    ##

    ['A', 'B', 0, 1, 2]


    #
    python3的新功能(yield from) def generator(): a = "AB" b = "22" yield from a yield from b g = generator() for i in g: print(i) ## A B 2 2

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

    #列表推导式
    egg_list = ["egg %d" %i for i in range(10)]
    print(egg_list)
    
    #或者用for循环
    egg_list = []
    for i in range(10):
        egg_list.append("egg %d" %i)
    print(egg_list)
    
    print([i*i for i in range(10)])
    ###
    ['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']
    ['egg 0', 'egg 1', 'egg 2', 'egg 3', 'egg 4', 'egg 5', 'egg 6', 'egg 7', 'egg 8', 'egg 9']
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    ##生成器表达式

    #生成器表达式
    g = (i for i in range(10))
    print(g) #返回一个生成器,几乎不占用内存
    for i in g:
        print(i)
    ##
    <generator object <genexpr> at 0x00000262E6CE6308>
    0
    1
    2
    3
    4
    5
    6
    7
    8
    9

     ###各种推导式:

    [满足条件的元素  for  元素  in  可迭代的数据类型  if  元素满足的相关条件 ] #筛选功能

    例一:30以内所有能被3整除的数

    multiples = [i for i in range(30) if i % 3 is 0]
    print(multiples)
    # Output: [0, 3, 6, 9, 12, 15, 18, 21, 24, 27]

    例二:30以内所有能被3整除的数的平方

    def squared(x):
        return x*x
    multiples = [squared(i) for i in range(30) if i % 3 is 0]
    print(multiples)

    或者:
    lst = [i*i for i in range(30) if i % 3 ==0]
    print(lst)
    ##
    [0, 9, 36, 81, 144, 225, 324, 441, 576, 729]

    例三:找到嵌套列表中名字含有两个‘e’的所有名字

    names = [['Tom', 'Billy', 'Jefferson', 'Andrew', 'Wesley', 'Steven', 'Joe'],
             ['Alice', 'Jill', 'Ana', 'Wendy', 'Jennifer', 'Sherry', 'Eva']]
    
    print([name for lst in names for name in lst if name.count('e') >= 2])  # 注意遍历顺序,这是实现的关键
     
    #['Jefferson', 'Wesley', 'Steven', 'Jennifer']
    
    
  • 相关阅读:
    Golang 接口(interface)
    Golang 结构体(struct)
    Golang fmt包介绍
    Golang的函数(func)
    Golang数据类型 (map)
    Golang 指针(pointer)
    Golang数据类型 切片(slice)
    操作系统学习笔记(五) 页面置换算法
    Python 元组、列表
    操作系统学习笔记(四) 存储模型和虚拟内存
  • 原文地址:https://www.cnblogs.com/shaopan/p/10015997.html
Copyright © 2020-2023  润新知