zoukankan      html  css  js  c++  java
  • 可迭代的对象,迭代器和生成器

    可迭代对象

    迭代是数据处理的基石。

      扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式。

    迭代器模式按需一次获取一个数据项

    序列可以迭代的原因:iter 函数

      解释器需要迭代对象X时,会自动调用 iter(x)

      内置的iter函数作用有:

        (1)检查对象时候有__iter__方法,如果实现了就调用它,获取迭代器。

        (2)如果没有实现__iter__方法,但是实现了__getitem__方法,Python会创建一个迭代器,尝试按顺序(从索引0开始)获取元素

        (3)如果尝试失败,Python会抛出TypeError异常。

      任何序列都可以迭代的原因是,它们都实现了__getitem__方法,通常情况下,标准序列都会实现__iter__方法。(鸭子类型体现)

    检查对象是否可以迭代:

      (1)iter(X)       # 考虑__getitem__方法

      (2)issubclass(X,abc.Iterable)  # 不考虑__getitem__方法

    可迭代的对象:

      使用 iter() 内置函数可以获取迭代器的对象。(实现了__iter__方法,或者实现__getitem__方法,而且参数从0开始)

      Python从可迭代的对象中获取迭代器。

    For循环实质:

      利用While循环模式

    >>> s = 'ABC'
    >>> it = iter(s)
    >>> while True:
      try:
        print(next(it))
      except StopIteration:
        del it
        break      
    

      (1)使用可迭代对象获取迭代器it

      (2)不断在迭代器上调用next函数,获取下一个字符。

      (3)如果没有字符了,迭代器会抛出StopIteration异常。

      (4)释放对it的引用。

      (5)退出循环

    迭代器

    迭代器接口:

      (1)__next__ 返回下一个可用的元素,如果没有元素了,抛出StopIteration异常。

      (2)__iter__ 返回self。迭代器也可以迭代

    误区:

      迭代器可以迭代,但是可迭代的对象不是迭代器。

      可迭代对象必须实现__iter__方法,但不能实现__next__方法。

      迭代器的__iter__方法应该返回自身。

    生成器

    生成器函数:

      使用 yield 关键字构建。

      生成器函数不会抛出StopIteration异常,而是在生成完全部数值后直接退出。

      只要Python函数定义体中有yield关键字,该函数就是生成器函数。

      调用生成器函数时,会返回一个生成器对象。

    def gen_123():
        yield 1
        yield 2
        yield 3
    
    >>> gen_123
    <function gen_123 at 0x00000166BD887F28>
    >>> gen_123()
    <generator object gen_123 at 0x00000166BD9BDF68>
    

      

    生成器表达式:

      列表推导的惰性版本:不会迫切的构建列表没事返回一个生成器,按需惰性生成元素。

    >>> def gen_AB():
        print('Start')
        yield 'A'
        print('Continue')
        yield 'B'
        print('End')
    
    >>> res1 = [x*3 for x in gen_AB()]
    Start
    Continue
    End
    >>>for i in res1:
        print(i)
    AAA
    BBB
    

      列表推导迫切地迭代gen_AB()函数生成的生成器对象,产出元素。

    >>> res2 = (x*3 for x in gen_AB())
    >>> res2
    <generator object <genexpr> at 0x000001D48970F2B0>
    >>> for i in res2:
            print('-->',i)
    Start
    --> AAA
    Continue
    --> BBB
    End
    

      

    ▲ 所有生成器都是迭代器,生成器完全实现了迭代器接口。

    ▲ 迭代器用于从集合中取出元素;生成器用于“凭空”生成元素。

    ▲ Python社区中大多数时候都把迭代器和生成器视作同一个概念

    (1)等差数列生成器例子:

    >>> ap = ArithmeticProgression(0,1,3)
    >>> list(ap)
    [0,1,2]
    >>> from fractions import Fraction
    >>> ap = ArithmeticProgression(0,Fraction(1,3),1)
    >>> list(ap)
    [Fraction(0,3),Fraction(1,3),Fraction(2,3)]
    >>> from decimal import Decimal
    >>> ap = ArithmeticProgression(0,Decimal('.1'),.3)
    >>> list(ap)
    [Decimal('0.0'),Decimal('0.1'),Decimal('0.2')]
    class ArithmeticProgression:
    
        def __init__(self,begin,step,end=None):
    
            self.begin = begin
            self.step = step
            self.end = end
    
        def __iter__(self):
    
            forever = self.end is None
            index = 0
            result = type(self.begin + self.step)(self.begin)
    
            while forever or index < self.end :
                yield result
                index += 1
                result = self.begin + index * self.step
    
    
    ##### 函数版本:
    def aritprog_gen(begin,step,end):
        
        forever = end is None
        index = 0
        result = type(begin +step)(begin)
    
        while forever or index < end:
            yield result
            index += 1
            result = begin + index * step
    ArithmeticProgression

    (2)使用 itertools模块生成等差数列

    itertools.count 函数返回的生成器能生成多个数。然而,itertools.count 函数从不停止,因此调用 list(count()),会超出可以内存。

    itertools.takewhile 函数会生成一个使用另一个生成器的生成器,在指定的条件计算结果为False时停止。因此,两个函数可以结合使用。

    >>> gen = itertools.takewhile(lambda x : x < 3 , itertools.count(1,.5))
    >>> list(gen)
    [1,1.5,2.0,2.5]
    

      

    标准库中的生成器函数

    标准库中的生成器函数:

    一、用于过滤的生成器函数 :从输入的可迭代对象中产出元素的子集,而且不修改元素本身。

    (1)itertools.compress(it, selector_it),并行处理两个可迭代的对象,如果 selector_it中的元素是真值,产出it中对应的元素。

    (2)itertools.dropwhile(predicate, it),处理it,跳过predicate计算结果为真值的元素,产出剩下的各个元素。

    (3)filter(predicate, it),把it中的各个元素传给predicate,如果predicate(item)返回True,产出对应的元素。

    (4)itertools.filterfalse(predicate, it),如果predicate(item)返回False,产出对应的元素。

    (5)itertools.islice(it, stop) 或 itertools.islice(it, start, stop, step=1),产出it的切片。

    (6)itertools.takewhile(predicate, it),predicate返回True时产出对应的元素,然后立即停止,不再继续检查。

    >>> import itertools
    >>>
    >>> def vowel(c):
    ...     return c.lower() in 'aeiou'
    ...
    >>> list(itertools.compress('Aardvark',(1,0,1,1,1,0)))
    ['A', 'r', 'd', 'v']
    >>> list(itertools.dropwhile(vowel,'Aardvark'))
    ['r', 'd', 'v', 'a', 'r', 'k']
    >>> list(filter(vowel,'Aardvark'))
    ['A', 'a', 'a']
    >>> list(itertools.filterfalse(vowel,'Aardvark'))
    ['r', 'd', 'v', 'r', 'k']
    >>> list(itertools.islice('Aardvark',4))
    ['A', 'a', 'r', 'd']
    >>> list(itertools.islice('Aardvark',1,7,2))
    ['a', 'd', 'a']
    >>> list(itertools.takewhile(vowel,'Aardvark'))
    ['A', 'a']

     二、用于映射的生成器函数 :对输入的可迭代对象中的元素做计算,然后返回结果。

    (1)itertools.accumulate(it, [func]),产出累积的总和,如果提供了func,那么把前两个元素传给它,然后把计算结果和下一个元素传给它。

    (2)enumerate(iterable, start=0),产出由两个元素组成的元组,结构是(index, item),其中index从start开始计数,item则从iterable中获取。

    (3)map(func, it1,[it2, ..., itn]),把it中的各个元素传给func,产出结果。如果传入N个可迭代的对象,func必须能接受N个参数,要并行处理各个可迭代对象。

    (4)itertools.starmap(func, it),把it中的各个元素传给func,产出结果。输入的可迭代对象应该产出可迭代的元素iit,然后以func(*iit),形式调用func。

    >>> import itertools
    >>> sample = [5,4,2,8,7,6,3,0,9,1]
    >>> list(itertools.accumulate(sample))
    [5, 9, 11, 19, 26, 32, 35, 35, 44, 45]
    >>> list(itertools.accumulate(sample,min))
    [5, 4, 2, 2, 2, 2, 2, 0, 0, 0]
    >>> list(itertools.accumulate(sample,max))
    [5, 5, 5, 8, 8, 8, 8, 8, 9, 9]
    >>> import operator
    >>> list(itertools.accumulate(range(1,11),operator.mul))
    [1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800]
    
    >>> list(enumerate('Aardvark',1))
    [(1, 'A'), (2, 'a'), (3, 'r'), (4, 'd'), (5, 'v'), (6, 'a'), (7, 'r'), (8, 'k')]
    
    >>> list(map(operator.mul,range(0,11),[2,4,8]))
    [0, 4, 16]
    >>> list(map(lambda a,b:(a,b),range(0,11),(2,4,8)))
    [(0, 2), (1, 4), (2, 8)]
    
    >>> list(itertools.starmap(operator.mul,enumerate('Aardvark',1)))
    ['A', 'aa', 'rrr', 'dddd', 'vvvvv', 'aaaaaa', 'rrrrrrr', 'kkkkkkkk']
    >>> list(itertools.starmap(lambda a,b:b/a,enumerate(itertools.accumulate(sample),1)))
    [5.0, 4.5, 3.6666666666666665, 4.75, 5.2, 5.333333333333333, 5.0, 4.375, 4.888888888888889, 4.5]

    三、用于合并的生成器函数 :从输入的多个可迭代对象中产出元素。

    (1)itertools.chain(it1, ..., itN),先产出it1中的所有元素,然后产出it2,以此类推,连接在一起。

    (2)itertools.chain.from_iterable(it),产出it生成的各个可迭代对象中的元素,一个一个连接在一起。

    (3)itertools.product(it1, ..., itN, repeat=1),计算笛卡尔积,repeat指名重复处理多少次输入的可迭代对象

    (4)zip(it1 , ..., itN),从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组。只要有一个可迭代对象到头就停止。

    (5)itertools.zip_longest(it1, ..., itN, fillvalue=None),从输入的各个可迭代对象中获取元素,产出由N个元素组成的元组。空缺值用fillvalue填充。

    >>> list(itertools.chain.from_iterable('ABC'))
    ['A', 'B', 'C']
    >>> list(itertools.chain.from_iterable(enumerate('ABCD',1)))
    [1, 'A', 2, 'B', 3, 'C', 4, 'D']
    >>> list(itertools.product('ABC',range(2)))
    [('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]
    >>> list(itertools.product('ABC',repeat=2))
    [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'B'), ('B', 'C'), ('C', 'A'), ('C', 'B'), ('C', 'C')]
    >>> list(zip(range(0,10),'ABCD'))
    [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D')]
    >>> list(itertools.zip_longest(range(0,10),'ABCD',))
    [(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, None), (5, None), (6, None), (7, None), (8, None), (9, None)]

    四、用于扩展输入的可迭代对象:从一个元素中产出多个值。

    (1)itertools.combinations(it, out_len),把it产出的out_len个元素组合在一起,然后产出。

    (2)itertools.combinations_with_replacement(it, out_len),把it产出的out_len个元素组合在一起,然后产出。包含相同元素的组合。

    (3)itertools.count(start=0, step=1),从start开始不断产出数字,按step指定的步幅增加。

    (4)itertools.cycle(it),从it中产出各个元素,存储各个元素的副本,然后按顺序重复不断地产出各个元素。

    (5)itertools.permutations(it, out_len=None),把out_len个it产出的元素排列在一起,然后产出这些排列。(注重顺序)

    (6)itertools.repeat(item, [times]),重复不断地产出指定的元素,除非提供times,指定次数。

    >>> list(itertools.combinations('ABC',2))
    [('A', 'B'), ('A', 'C'), ('B', 'C')]
    >>> list(itertools.combinations_with_replacement('ABC',2))
    [('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]
    >>> list(itertools.permutations('ABC',2))
    [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
    >>> list(itertools.repeat('ABC',2))
    ['ABC', 'ABC']
    >>> cy = itertools.cycle('ABC')
    >>> list(itertools.islice(cy,7))
    ['A', 'B', 'C', 'A', 'B', 'C', 'A']

    五、用于重新排列的生成器

    (1)itertools.groupby(it, key=None),产出由两个元素组成的元素,形式为(key,group)其中key是分组标准。(假定输入的可迭代对象使用分组标准排序)

    (2)reversed(seq),从后向前,倒序产出seq中的元素,seq必须是序列,或者实现了__reversed__特殊方法的对象。

    (3)itertools.tee(it, n=2),产出一个由n个生成器组成的元祖,每个生成器用于单独产出输入的可迭代对象中的元素

    >>> list(itertools.groupby('LLLAAGGG'))
    [('L', <itertools._grouper object at 0x000001AB8949B860>), 
    ('A', <itertools._grouper object at 0x000001AB8949B898>),
    ('G', <itertools._grouper object at 0x000001AB894990F0>)] >>> animals = ['duck','eagle','rat','giraffe','bear','bat','dolphin'] >>> animals.sort(key=len) >>> for length,group in itertools.groupby(animals,len): ... print(length,'->',list(group)) 3 -> ['rat', 'bat'] 4 -> ['duck', 'bear'] 5 -> ['eagle'] 7 -> ['giraffe', 'dolphin'] >>> list(itertools.tee('ABC')) [<itertools._tee object at 0x000001AB89494EC8>, <itertools._tee object at 0x000001AB89498488>] >>> list(zip(*itertools.tee('ABC'))) [('A', 'A'), ('B', 'B'), ('C', 'C')]

    yield from

    yield from句法:

      生成器函数需要产出另一个生成器生成的值,传统解决方法是使用嵌套的for循环。

    def chain(*iterables):
        for it in iterables:
            for i in it:
                yield i
    
    s = 'ABC'
    t = tuple(range(3))
    
    print(list(chain(s,t)))
    # ['A', 'B', 'C', 0, 1, 2]

      使用yield from 代替内层for:

    def chain(*iterables):
        for it in iterables:
            yield from it

    归约 /合拢 /累加函数 

    可迭代的归约函数 /合拢函数 /累加函数 :

    (1)all(it),it中的所有元素都为真值时返回True,否则返回False;all([]) 返回 True、all(0) 返回 False

    (2)any(it),it中的有元素为真值时返回True,否则返回False; any([]) 返回  False、any([0,0.0]) 返回 False

    (3)max(it, [key=,], [default=]),返回it中最大值,key是排序函数,与sorted函数一样,如果可迭代对象为空,返回default。

    (4)min(it, [key=,], [default=]),返回it中最小值,key是排序函数,与sorted函数一样,如果可迭代对象为空,返回default。

    (5)functools.reduce(func, it, [initial]),把前两个元素传给func,然后把计算结果和第三个元素传给func,以此类推,返回最后的结果,initial当作第一个元素传入。

    (6)sum(it, start=0),it中所有元素的总和,如果提供start,会先把它加上。

    >>> g = (n for n in [0,0.0,7,8])
    >>> any(g)
    True
    >>> next(g)
    8

    深入 iter 函数

    深入分析 iter 函数 :

      在Python中迭代对象x时会调用 iter(x)。

      iter函数还可以传入第二个参数。哨符,这是个标记值,当可调用的对象返回这个值时,触发迭代器抛出StopIteration异常,而不产出哨符。

    def d6():
        return random.randint(1,6)
    
    d6_iter = iter(d6,1)
    for roll in d6_iter:
        print(roll)

      用来处理读取文件,直到遇到空行或者到达文件末尾为止:

    with open('mydata.bat') as fp:
        for line in iter(fp.readline, '
    '):
            process_line(line)
  • 相关阅读:
    sql 修改表名、列名、列类型
    .Net WinForm下配置Log4Net(总结不输出原因)
    ubuntu20.04 搭建门罗币节点
    python2 和 python3里StringIO和BytesIO的区别
    java.lang.IllegalArgumentException: java.lang.ClassCastException
    iphoneX安全边界
    ios中禁用回弹效果
    将nodejs回调方法变为promise
    实现trim方法
    flex实现三个div上中下布局
  • 原文地址:https://www.cnblogs.com/5poi/p/11270377.html
Copyright © 2011-2022 走看看