迭代器和生成器(2)--python cookbook

版权声明:zhiyu https://blog.csdn.net/ichglauben/article/details/81980560

跳过可迭代对象的开始部分

  • itertools 模块中有一些函数可以完成这个任务。 首先介绍的是 itertools.dropwhile() 函数。使用时,你给它传递一个函数对象和一个可迭代对象。 它会返回一个迭代器对象,丢弃原有序列中直到函数返回Flase之前的所有元素,然后返回后面所有元素。
>>> with open('/etc/passwd') as f:
...   for i in f:
...     print(i,end='')
... 
  • 如果你想跳过开始部分的注释行的话,可以这样做:
>>> from itertools import dropwhile
>>> with open('/etc/passwd') as f:
...    for i in dropwhile(lambda i: i.startswith('#'),f):
...      print(i,end='')
... 
...
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
...
>>> from itertools import islice
>>> items = ['a','b','c',1,2,3]
>>> for x in islice(items,3,None):
...   print('{} '.format(x),end='')
...  
1 2 3 >>> 
  • islice() 函数最后那个 None 参数指定了你要获取从第3个到最后的所有元素, 如果 None 和3的位置对调,意思就是仅仅获取前三个元素恰恰相反, (这个跟切片的相反操作 [3:] 和 [:3] 原理是一样的)。
>>> with open('/etc/passwd') as f:
...   lines = (line for line in f if not line.startswith('#'))
...   for line in lines:
...     print('{}'.format(line),end='')
... 

这样写确实可以跳过开始部分的注释行,但是同样也会跳过文件中其他所有的注释行。 换句话讲,我们的解决方案是仅仅跳过开始部分满足测试条件的行,在那以后,所有的元素不再进行测试和过滤了。

排列组合的迭代

  • itertools模块提供了三个函数来解决这类问题。 其中一个是 itertools.permutations() , 它接受一个集合并产生一个元组序列,每个元组由集合中所有元素的一个可能排列组成。 也就是说通过打乱集合中元素排列顺序生成一个元组
>>> items = ['a', 'b', 'c']
>>> from itertools import permutations
>>> for p in permutations(items):
...  print(p)
... 
('a', 'b', 'c')
('a', 'c', 'b')
('b', 'a', 'c')
('b', 'c', 'a')
('c', 'a', 'b')
('c', 'b', 'a')

限定个数

>>> for i in permutations(items,2):
...   print(i)
... 
('a', 'b')
('a', 'c')
('b', 'a')
('b', 'c')
('c', 'a')
('c', 'b')

使用 itertools.combinations() 可得到输入集合中元素的所有的组合:

>>> from itertools import combinations
>>> items = ['a','b','c']
>>> for i in combinations(items,2):
...   print(i)
... 
('a', 'b')
('a', 'c')
('b', 'c')
>>> for i in combinations(items,3):
...   print(i)
... 
('a', 'b', 'c')
>>> for i in combinations(items,1):
...   print(i)
... 
('a',)
('b',)
('c',)
>>> 
  • 在计算组合的时候,一旦元素被选取就会从候选中剔除掉(比如如果元素’a’已经被选取了,那么接下来就不会再考虑它了)。 而函数 itertools.combinations_with_replacement() 允许同一个元素被选择多次,
>>> from itertools import combinations_with_replacement
>>> for i in combinations_with_replacement(items,3):
...   print(i)
... 
('a', 'a', 'a')
('a', 'a', 'b')
('a', 'a', 'c')
('a', 'b', 'b')
('a', 'b', 'c')
('a', 'c', 'c')
('b', 'b', 'b')
('b', 'b', 'c')
('b', 'c', 'c')
('c', 'c', 'c')

序列上索引值迭代

在迭代一个序列的同时跟踪正在被处理的元素索引。

解决方案:
使用内置的enumerate()函数可以很好的解决这个问题:

>>> my_list = ['a','b','c']
>>> for idx,val in enumerate(my_list):
...   print(idx,val)
... 
0 a
1 b
2 c
  • 为了按传统行号输出(行号从1开始),你可以传递一个开始参数:
>>> for idx,val in enumerate(my_list,1):
...   print(idx,val)
... 
1 a
2 b
3 c
>>> 
  • 这种情况在你遍历文件时想在错误消息中使用行号定位时候非常有用:
  • enumerate() 对于跟踪某些值在列表中出现的位置是很有用的。 所以,如果你想将一个文件中出现的单词映射到它出现的行号上去,可以很容易的利用 enumerate() 来完成:
#! /usr/bin/python3
from collections import defaultdict
word_summary = defaultdict(list)

with open('/etc/passwd','r') as f:
  lines = f.readlines()

for idx,line in enumerate(lines):
  words = [w.strip().lower() for w in line.split()]
  for word in words:
    word_summary[word].append(idx)
print(word_summary)

defaultdict(<class 'list'>, {'root:x:0:0:root:/root:/bin/bash': [0], 
'bin:x:1:1:bin:/bin:/sbin/nologin': [1],
 'daemon:x:2:2:daemon:/sbin:/sbin/nologin': [2],
 'adm:x:3:4:adm:/var/adm:/sbin/nologin': [3], 
 'lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin': [4],
 'sync:x:5:0:sync:/sbin:/bin/sync': [5],
 'shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown': [6],
 'halt:x:7:0:halt:/sbin:/sbin/halt': [7], 
 'mail:x:8:12:mail:/var/spool/mail:/sbin/nologin': [8], 
 'operator:x:11:0:operator:/root:/sbin/nologin': [9],
 'games:x:12:100:games:/usr/games:/sbin/nologin': [10],
 'ftp:x:14:50:ftp': [11], 'user:/var/ftp:/sbin/nologin': [11],
 'nobody:x:99:99:nobody:/:/sbin/nologin': [12], 
 'systemd-bus-proxy:x:999:998:systemd': [13],  'bus': [13],  'proxy:/:/sbin/nologin': [13], 
 'systemd-network:x:192:192:systemd': [14],'network': [14], 'management:/:/sbin/nologin': [14], 
 'dbus:x:81:81:system': [15], 'message': [15], 'bus:/:/sbin/nologin': [15], 
 'polkitd:x:998:997:user': [16], 'for': [16], 'polkitd:/:/sbin/nologin': [16], 
 'tss:x:59:59:account': [17], 'used': [17], 'by': [17], 'the': [17, 17], 'trousers': [17], 'package': [17], 'to': [17], 'sandbox': [17], 'tcsd': [17], 'daemon:/dev/null:/sbin/nologin': [17],
 'sshd:x:74:74:privilege-separated': [18], 'ssh:/var/empty/sshd:/sbin/nologin': [18],
 'postfix:x:89:89::/var/spool/postfix:/sbin/nologin': [19], 'chrony:x:997:995::/var/lib/chrony:/sbin/nologin': [20], 
 'ntp:x:38:38::/etc/ntp:/sbin/nologin': [21], 'nscd:x:28:28:nscd': [22], 'daemon:/:/sbin/nologin': [22], 
 'tcpdump:x:72:72::/:/sbin/nologin': [23], 
 'admin:x:1000:1000::/home/admin:/bin/bash': [24],
 'pythonx:x:1001:1001::/home/pythonx:/bin/bash': [25]})
  • 处理完文件后打印 word_summary ,会发现它是一个字典(准确来讲是一个 defaultdict ), 对于每个单词有一个 key ,每个 key 对应的值是一个由这个单词出现的行号组成的列表。
  • 如果某个单词在一行中出现过两次,那么这个行号也会出现两次, 同时也可以作为文本的一个简单统计。


  • defaultdict()初始化应用(list int)
    (1)

from collectionsimport defaultdict
s = [('yellow',1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]
d =defaultdict(list)
for k,v in s:

       d[k].append(v)

print(d.items()
[('blue', [2, 4]),('red', [1]), ('yellow', [1, 3])]

(2)

s = 'mississippi'

d =defaultdict(int)

for k in s:

       d[k] += 1

print(d.items())
[('i', 4), ('p',2), ('s', 4), ('m', 1)]

每个字典的key对应的value都是其出现的次数

  • read() readlines() readline()
    .readlines()自动将文件内容分析成一个行的列表
    .readline()每次只读取一行,通常比 .readlines()慢得多

  • 额外定义一个计数变量的时候,使用 enumerate() 函数会更加简单
>>> with open('/etc/passwd','r') as f:
...   for lineno,line in enumerate(f):
...     print(lineno,line,end='')
... 
0 root:x:0:0:root:/root:/bin/bash
1 bin:x:1:1:bin:/bin:/sbin/nologin
2 daemon:x:2:2:daemon:/sbin:/sbin/nologin
3 adm:x:3:4:adm:/var/adm:/sbin/nologin
4 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
5 sync:x:5:0:sync:/sbin:/bin/sync
6 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
7 halt:x:7:0:halt:/sbin:/sbin/halt
8 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
9 operator:x:11:0:operator:/root:/sbin/nologin
10 games:x:12:100:games:/usr/games:/sbin/nologin
11 ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
12 nobody:x:99:99:Nobody:/:/sbin/nologin
13 systemd-bus-proxy:x:999:998:systemd Bus Proxy:/:/sbin/nologin
14 systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
15 dbus:x:81:81:System message bus:/:/sbin/nologin
16 polkitd:x:998:997:User for polkitd:/:/sbin/nologin
17 tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
18 sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
19 postfix:x:89:89::/var/spool/postfix:/sbin/nologin
20 chrony:x:997:995::/var/lib/chrony:/sbin/nologin
21 ntp:x:38:38::/etc/ntp:/sbin/nologin
22 nscd:x:28:28:NSCD Daemon:/:/sbin/nologin
23 tcpdump:x:72:72::/:/sbin/nologin
24 admin:x:1000:1000::/home/admin:/bin/bash
25 pythonx:x:1001:1001::/home/pythonx:/bin/bash
  • 有时候当你在一个已经解压后的元组序列上使用 enumerate() 函数时很容易调入陷阱。 你得像下面正确的方式这样写:
>>> data = [(1,2),(3,4),(5,6)]
>>> for n,(x,y) in enumerate(data):
...   print(n,(x,y),end='')
... 
0 (1, 2)1 (3, 4)2 (5, 6)

同时迭代多个序列

  • solve:
    为了同时迭代多个序列,使用 zip() 函数。比如:
>>> xpts = [1,5,4,2,10,7]
>>> ypts = [101,78,45,777,452,12]
>>> for x,y in zip(xpts,ypts):
...   print(x,y)
... 
1 101
5 78
4 45
2 777
10 452
7 12
  • 变元组
>>> for x in zip(xpts,ypts):
...   print(x)
... 
(1, 101)
(5, 78)
(4, 45)
(2, 777)
(10, 452)
(7, 12)
  • 如果这个不是你想要的效果,那么还可以使用 itertools.zip_longest() 函数来代替。
>>> from itertools import zip_longest
>>> for i in zip_longest(xpts,ypts):
...   print(i)
... 
(1, 101)
(5, 78)
(4, 45)
(2, 777)
(10, 452)
(7, 12)
(None, 13)
>>> for i in zip_longest(xpts,ypts,fillvalue=1):
...   print(i)
... 
(1, 101)
(5, 78)
(4, 45)
(2, 777)
(10, 452)
(7, 12)
(1, 13)
>>> s = dict(zip(headers,values))
>>> print(s)
{'name': 'ACME', 'shares': 10, 'price': 410.235}
>>> for name,val in zip(headers,values):
...   print(name,'=',val)
... 
name = ACME
shares = 10
price = 410.235

不同集合上元素的迭代

你想在多个对象执行相同的操作,但是这些对象在不同的容器中,你希望代码在不失可读性的情况下避免写重复的循环。

  • 解决办法
>>> from itertools import chain
>>> a = [1,2,3,4]
>>> b = ['x','y','z']
>>> for x in chain(a,b):
...   print(x)
... 
1
2
3
4
x
y
z
  • 使用 chain() 的一个常见场景是当你想对不同的集合中所有元素执行某些操作的时候。
>>> active_items = set('active')
>>> inactive_items = set('inactive')
>>> for item in chain(active_items,inactive_items):
...   print(item)
... 
i
t
c
a
v
e
i
n
t
c
a
v
e

猜你喜欢

转载自blog.csdn.net/ichglauben/article/details/81980560