python3迭代器和生成器

一、手動訪問迭代器中的元素

#要手動訪問迭代器中的元素,可使用next()函數
In [3]: with open('/etc/passwd') as f:
   ...:     try:
   ...:         while True:
   ...:             print(next(f))   #next()函數訪問迭代中的函數
   ...:     except StopIteration:  #捕獲結束異常
   ...:         print('None')
  
#經過指定返回結束值來判斷迭代結束
In [28]: with open('/etc/passwd') as f:
    ...:     while True:
    ...:         line = next(f,None)
    ...:         if line is None:
    ...:             break
    ...:         print(line)

二、委託迭代

class node(object):
    def __init__(self,value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self,node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children)


if __name__ == '__main__':
    root = node(0)
    child1 = node(1)
    child2 = node(2)
    root.add_child(child1)
    root.add_child(child2)
    for i in root:
        print(i)

#
Node(1)
Node(2)

三、用生成器建立新的迭代模式

def frange(start,stop,setup):  #建立新的迭代函數
    while start < stop:
        yield start    #經過yield轉換成一個生成器
        start += setup
#可根據要求生成迭代對象元素
for i in frange(0,10,0.8):
    print(i)

四、實現迭代協議

class Node:
    def __init__(self,value):
        self._value = value
        self._children = []

    def __repr__(self):
        return 'Node({!r})'.format(self._value)

    def add_child(self,node):
        self._children.append(node)

    def __iter__(self):
        return iter(self._children)

    def depth_first(self):  #首先輸出本身而後輸出本身的子節點
        yield self
        for i in self:
            yield from i.depth_first()

if __name__ == '__main__':
    root = Node(123)
    child1 = Node(10)
    child2 = Node(20)
    root.add_child(child1)
    root.add_child(child2)
    child1.add_child(Node(111))
    child1.add_child(Node(222))
    child2.add_child(Node(333))

    for i in root.depth_first():
        print(i)

#
Node(123)
Node(10)
Node(111)
Node(222)
Node(20)
Node(333)

五、反向迭代

In [29]: a = [1,2,3,4]

In [30]: for i in reversed(a):  #反向迭代
    ...:     print(i)
    ...:     
4
3
2
1

六、對迭代器作切片操做

#要對迭代器和生成器作數據切換處理,可使用itertools.islice()函數
In [31]: def count(n):
    ...:     while True:
    ...:         yield n
    ...:         n += 1
    ...:         
In [33]: c = count(0)
#生成器對象時不可切片操做的
In [34]: c[10:20]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-34-d27b6259daf3> in <module>()
----> 1 c[10:20]

TypeError: 'generator' object is not subscriptable 

#只有使用itertools.islice()函數纔可切片
In [35]: import itertools

In [36]: for i in itertools.islice(c,10,20):
    ...:     print(i,end=' ')
    ...:     
10 11 12 13 14 15 16 17 18 19 

#迭代器和生成器是無法執行普通的切片操做的,這是由於不知道它們的長度和索引,而islice()產生的結果是一個迭代器,它能夠產生出所須要切片元素,它是經過訪問並丟棄全部起始索引以前的元素來實現的,
#以後的元素會由islice對象來產生直到結束索引爲止。而且islice()會消耗掉提供的迭代器中的數據,它只能訪問一次的

七、跳過可迭代對象中的前一部分元素

#itertools模塊中有一個函數dropwhile()它回迭代丟棄須要過濾的元素,但只丟棄開頭的過濾元素
#cat test.txt

#aaa
#bbb
ccc
ddd
#eee

from itertools import dropwhile
#使用dropwhile函數過濾開始元素,startswith函數指定判斷元素的值
with open('test.txt') as f:
    for i in dropwhile(lambda x:x.startswith('#'),f):
        print(i,end='')

#輸出結構只判斷開始行中首字母爲#好的行將過濾掉
ccc
ddd
#eee

#也可用使用itertools模塊中的islice函數來指定跳過多少個元素
from itertools import islice
items = ['a','b','c',1,2,3]
for x in islice(items,4,None):
    print(x)

#輸出將跳過前面的4個元素,只輸出後面的2,3

#若是須要跳過全部須要顧慮的元素,只須要指定斷定規則值便可
with open('test.txt') as f:
    lines = (line for line in f if not line.startswith('#'))
    for line in lines:
        print(line,end='')

#輸出就只有不以#號開頭的行了
ccc
ddd

八、迭代全部可能的組合或排列

#itertools模塊中提供了3個函數來解決全部元素的重排列的可能狀況,先來看第一個itertools.permutations()使用方法:
In [6]: from itertools import permutations
In [7]: items = ['a','b','c']
In [8]: for i in permutations(items):
   ...:     print(i)
   ...:     
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

#若是要限定排序的長度可用指定長度參數
In [9]: for i in permutations(items,2):
   ...:     print(i)
....
('c', 'a')
('c', 'b')

#使用第二個函數itertools.combinations()它將輸出序列中全部元素的所有組合形式,但元素之間是不考慮順序的,好比(a,b)和(b,a)是一種組合形式將只出現一次
In [12]: for j in combinations(range(4),3):
    ...:     print(j)
    ...:     
(0, 1, 2)
(0, 1, 3)
(0, 2, 3)
(1, 2, 3)

In [13]: for j in combinations(range(4),4):
    ...:     print(j)
    ...:     
(0, 1, 2, 3)

#第三個函數itertools.combinations_with_replacement()它容許一個元素可被選擇屢次進行排序
In [14]: from itertools import combinations_with_replacement

In [15]: for i in combinations_with_replacement(range(1,3),3):
    ...:     print(i)
    ...:     
(1, 1, 1)
(1, 1, 2)
(1, 2, 2)
(2, 2, 2)

九、以索引-值對的形式迭代序列

#內建的enumerate()函數可用輸出元素序列,可用指定序列的開始值
In [20]: for k,v in enumerate(my_list,1):
    ...:     print(k,'==',v)
    ...:     
1 == a
2 == b
3 == c

#打印嵌套數組
In [21]: list1 = [(1,2),(11,22),(33,44)]

In [22]: for n,(k,v) in enumerate(list1,1):
    ...:     print(n,'<===>',k,v)
    ...:     
1 <===> 1 2
2 <===> 11 22
3 <===> 33 44

十、同時迭代多個序列

#同時迭代多個序列可用使用zip()函數,它將迭代對象產生出一個元組,整個迭代的長度取其中最短的序列長度
In [23]: list1 = [1,2,3,4,5]

In [24]: list2 = ['a','b','c','d']

In [25]: list3 = ['jar','py','jc']

In [26]: for x,y,z in zip(list1,list2,list3):
    ...:     print(x,y,z)
    ...:     
1 a jar
2 b py
3 c jc

#若是須要輸出完整的序列可使用itertools模塊中的zip_longest函數
In [27]: from itertools import zip_longest

In [28]: for i in zip_longest(list1,list2,list3):
    ...:     print(i)
    ...:     
(1, 'a', 'jar')
(2, 'b', 'py')
(3, 'c', 'jc')
(4, 'd', None)
(5, None, None)

#zip()一般用於處理數據配對的,如將以字典形式配對名稱和值
In [29]: heard = ['name','age','salely']

In [30]: values = ['zhangsan',30,99999]

In [31]: s = dict(zip(heard,values))

In [32]: s
Out[32]: {'name': 'zhangsan', 'age': 30, 'salely': 99999}

十一、在不一樣的容器中進行迭代

#itertools.chain()方法能夠在多個容器中迭代對象
In [33]: from itertools import chain

In [34]: a = [1,2,3]

In [35]: b = ['a','b','c']

In [36]: for i in chain(a,b):print(i)
1
2
3
a
b
c

十二、建立處理數據的管道

import os,fnmatch,gzip,bz2,re

def gen_find(filepat,top):
    for path,dirlist,filelist in os.walk(top):
        for name in fnmatch.filter(filelist,filepat):
            yield os.path.join(path,name)

def gen_opener(filenames):
    for filename in filenames:
        if filename.endswith('.gz'):
            f = gzip.open(filename,'rt')
        elif filename.endswith('.bz2'):
            f = bz2.open(filename,'rt')
        else:
            f = open(filename,'rt')
        yield f
        f.close()

def gen_concatenate(iterators):
    for it in iterators:
        yield from it

def gen_grep(pattern,lines):
    pat = re.compile(pattern)
    for line in lines:
        if pat.search(line):
            yield line

if __name__ == '__main__':
    file_name = input('please in to file:')
    directory_name = input('pease in to directory:')
    lognames = gen_find(file_name,directory_name)
    files = gen_opener(lognames)
    lines = gen_concatenate(files)
    pylines = gen_grep('(?i)python',lines)
    for line in pylines:
        print(line)

1三、扁平化處理嵌套型的序列

#yield from能夠將迭代對象中子元素進行遞歸,將它們全部的值產生出來,獲得的結果就是一個沒有嵌套的單值序列

from collections import Iterable

def flatten(items,ignore_types=(str,bytes)):
    for i in items:
        if isinstance(i,Iterable) and not isinstance(i,ignore_types):
            yield from flatten(i)
        else:
            yield(i)

items = [1,2,[3,4,[5,6],7],8]
for i in flatten(items):
    print(i,end=' ')

1四、合併多個有序序列,再對整個有序序列進行迭代

#對多個有序序列先進行合併再進行排序,可使用heapq.merge()函數,它不會將全部的數據讀取到堆中,也不會作排序操做,
#它只是簡單的檢查每一個輸入序列中的第一個元素,將最小的發送出去,而後再重複執行這一步操做,直到序列耗盡爲止
In [8]: import heapq In [9]: a = [12,3,4,9] In [10]: b = [5,32,15,1] In [11]: c = heapq.merge(a,b) In [12]: c Out[12]: <generator object merge at 0x7f781eb934f8> In [13]: d = list(c) In [14]: d Out[14]: [5, 12, 3, 4, 9, 32, 15, 1]

1五、用迭代器取代while循環

#咱們在處理I/O通訊時常常會碰到接收數據時判斷是否接收完的代碼
def readder(s):
    while True:
        data = s.recv(8192)
        if data == b'':
            break
#上面的代碼咱們可使用iter()函數來代替,它能夠選擇性的接收一個無參數的可調用對象和一個結束值做爲輸入,iter()會建立一個迭代器而後重複調用用戶提供的可調用對象,知道它返回結束值

import sys
def reader(s):
    for i in iter(lambda:s.recv(8192),b''):
        data = sys.stdout.write(i)
        print(data)
相關文章
相關標籤/搜索