迭代器(iterator)
在Python中,迭代(iteration)的概念是建立在容器的基础上的,所有的容器都是可迭代的(iterable)。容器是对象的集合,列表、元组、字典、集合等都是容器。不同容器之间的区别在于内部数据结构的实现方式不同。可迭代对象(列表、元组、字典、集合等)通过iter(iterable)函数返回一个迭代器,next(iterator)函数可实现在迭代器中遍历。next(iterator)函数要么返回当前对象的下一个对象,要么抛出StopIteration的错误。
container = [[1, 2, 3, 4], (1, 2, 3, 4), set([1, 2, 3, 4]), {1 : 'a', 2 : 'b'}, 'abcd']
for it in container:
it = iter(it)
print(it)
while True:
try:
print(next(it))
except StopIteration:
break
# 输出
# <list_iterator object at 0x7f5d5547a6d8>
# 1 2 3 4
# <tuple_iterator object at 0x7f5d5547a6d8>
# 1 2 3 4
# <set_iterator object at 0x7f5d553df8b8>
# 1 2 3 4
# <dict_keyiterator object at 0x7f5d55c62d18>
# 1 2
# <str_iterator object at 0x7f5d5530f278>
# a b c d
生成器(generator)
生成器实质上也是一个迭代器,只不过在迭代器中的数据都是已经存在的,而生成器是在需要数据时才临时生成数据。相比于迭代器,这样的特性有利于节省内存空间。
例如,[i for i in range(1000000)]生成一个包含一百万元素的列表,每个元素都存在于内存中,占据了大量的空间。但是我们有时候并不需要在内存同时保存这些元素,例如对列表中的所有元素求和,我们每次只需要知道下一个元素的值并对其进行累加,累加完后即可丢弃,然后依次迭代下去,就可以求出所有元素的和。 生成器的写法如下,和迭代器相比,只不过把中括号换成了小括号。
iterator = [i for i in range(1000000)] # 迭代器
generator = (i for i in range(1000000)) # 生成器
下面是两个生成器的例子,在Python中,带有yield关键字的函数称为生成器。当调用生成器时,每次遇到yield时函数会暂停并保存当前位置的信息,然后返回yield当前值。在下次执行next()时从之前保存的位置继续执行。
def generator(n):
i = 1
while i < n:
yield i
i += 1
g = generator(10)
print(g)
while True:
try:
print(next(g))
except StopIteration:
break
# 输出
# <generator object generator at 0x7ff57086b9e8>
# 1 2 3 4 5 6 7 8 9 10
def generator(s, l):
for i, v in enumerate(s):
if v == l:
yield i
g = generator('OrangeApple', 'e')
while True:
try:
print(next(g))
except StopIteration:
break
# 输出
# 5 10
现在有一个判断子序列的问题。如果第一个列表中的元素在第二个列表中依次出现,则第一个列表就是第二个列表的子序列。例如[1, 3, 5]是[1, 2, 3, 4, 5]的子序列,[1, 5, 3]不是[1, 2, 3, 4, 5]的子序列。
常规的解法是通过两个指针分别指向两个列表的开端,然后遍历第二个列表。当两个指针指向的元素相同时,就把第一个指针向后移动一位,直到指针指向最后一个元素的下一位时,返回True,否则返回False。
def isSubsequence(s, t):
p = 0
for i in range(len(t)):
if t[i] == s[p]:
p += 1
if p == len(s):
return True
return False
如果利用生成器来解,则只需要两行。
def is_subsequence(s, t):
t = iter(t)
return all(i in t for i in s)
首先通过t = iter(t)将t转化为一个迭代器,然后对s中的每个元素,依次判断该元素是否在t中。all()表示括号中的所有元素为True时才返回True。
这里i in t是代码的关键。表示依次遍历t中的元素,如果某个元素等于i,则返回True,否则遍历结束后返回False。如果遍历结束前某个元素等于i,则中断遍历,返回True。当第二次执行这条语句时(即判断下个元素是否在t中),将直接从上次遍历中断的位置继续执行。 也就是说生成器只能遍历一次,且不可逆。当遍历中断时,下次继续遍历只会从上次中断的位置开始,而不会从头开始。可以验证一下:
t = [1, 2, 3, 4, 5]
t = iter(t)
print(2 in t)
print(4 in t)
print(3 in t)
# 输出
# True
# True
# False