概念

  1. 使用for循环对列表、元组、字符串等类型挨个提取数据,该过程称之为遍历,也叫迭代
  2. 迭代器是一个包含数值的对象
  3. 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问结束,迭代器只前不后
  4. 迭代器可以记住遍历对象的位置,当调用next()方法时就会返回下一个值,做到随用随取,不会占用很多内存空间。
  5. 一个对象如果有__iter__()方法并且该方法返回值有__next__()方法,那么该对象就为迭代器
  6. 一个可迭代对象调用了__iter__()方法才返回迭代器

可迭代对象

for语句之所以能够遍历,是因为有迭代器的支持。for语句在遍历时调用遍历对象的__iter__()方法,如果遍历对象没有该方法就不能对其进行迭代,也就是不能遍历该对象

1
2
3
4
5
6
7
# 整型对象没有__iter__方法,不能被迭代

a = 123
print(a.__iter__())

# 结果
AttributeError: 'int' object has no attribute '__iter__'
1
2
3
4
5
6
7
8
9
10
11
12
13
# 字符型对象有__iter__方法,可以被迭代

a = "123"
print(a.__iter__())

for i in a:
print(i)

# 结果
<str_iterator object at 0x000002C2A413C8C8>
1
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 引入Iterable来判断对象是否可被迭代
from collections.abc import Iterable

num = 123
txt = "123"
lis = [1,2,3]

print(isinstance(num, Iterable))
print(isinstance(txt, Iterable))
print(isinstance(lis, Iterable))

# 结果
False
True
True

迭代器

如果对象有__iter__()方法时,for语句就会把__iter__()方法的返回值当成一个对象,并且调用这个对象的__next__()方法。如果可迭代对象有__iter__()方法并且该方法返回值有__next__()方法,那么该对象就为迭代器

可迭代对象调用完__iter__()方法后才返回迭代器,可迭代对象不调用__iter__()方法就不是迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 引入Iterator来判断对象是否为迭代器
from collections.abc import Iterator

num = 123
txt = "123" # 可迭代对象而不是迭代器
lis = [1,2,3]

print(isinstance(num, Iterator))
print(isinstance(txt, Iterator))
print(isinstance(lis, Iterator))

# 结果
False
False
False

使用iter()方法可以获得可迭代对象的迭代器,下述代码返回True表示使用iter()方法后返回的对象为迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from collections.abc import Iterator

txt = iter("123") # 迭代器
lis = iter([1,2,3])

print(txt)
print(lis)
print(isinstance(txt, Iterator))
print(isinstance(lis, Iterator))

# 结果
<str_iterator object at 0x000002E94EAA8F08>
<list_iterator object at 0x000002E94EAEC748>
True
True

如果为迭代器则可以使用__next__()方法或next()方法来查看每一次的返回值,如果可迭代对象被遍历完之后再进行迭代则会抛出异常 。for循环就相当于创建的迭代对象的迭代器,并循环执行每个迭代器的__next__()方法

1
2
3
4
5
6
7
8
9
10
11
12
lis = iter([1,2,3])

print(lis.__next__())
print(next(lis))

# for i in lis:
# print(i)


# 结果
1
2

创建迭代器

创建一个类来作为迭代器,在类中定义__iter__()方法并返回迭代器,在类中定义__next__()方法并对传递进来的数据进行处理。为了防止迭代一直进行,可以在__next__()方法中使用raise StopIteration抛出异常终止迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 方法1

class MyIterator(object):
"""迭代器,对传递进来的数据反向输出"""
def __init__(self, data):
self.data = data
self.index = len(data)

def __iter__(self):
return self

def __next__(self):
# 当迭代对象被历遍完成后就抛出异常
if self.index == 0:
raise StopIteration

self.index -= 1
return self.data[self.index]

lis = [1,2,3,4,5]
my_iterator = MyIterator(lis)
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator))
print(next(my_iterator)) # 或者使用for遍历

# 结果
5
4
3
2
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 方法2

class MyNumber(object):
"""迭代对象"""
def __init__(self, data):
self.data = data

def __iter__(self):
return MyIterator(self.data) # 返回迭代器


class MyIterator(object):
"""迭代器,对传递进来的数据反向输出"""
def __init__(self, data):
self.data = data
self.index = len(data)

def __next__(self):
# 当迭代对象被历遍完成后就抛出异常
if self.index == 0:
raise StopIteration

self.index -= 1
return self.data[self.index]

lis = [1,2,3,4,5]
my_number = MyNumber(lis)

for i in my_number:
print(i)

# 结果
5
4
3
2
1

应用场景

场景

  1. 数列的数据过于庞大时
  2. 数列有规律,但不能通过列表推导式表

斐波那契数列

介绍

数列中的第1个元素为0,第二个元素为1,第n个元素为前两个之和,如0 1 1 2 3 5 8 13

不使用迭代器

不使用迭代器时直接将运算结果写到列表中,再进行遍历。如果需要运算的斐波那契范围庞大,则需要等待运算完成后才能进行遍历(浪费时间),遍历前要把庞大的运算结果写入列表(浪费内存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
num_list = []   # 存储斐波那契数列
a = 0 # 第1个元素
b = 1 # 第2个元素
count = 0

# 实现斐波那契数列的运算(10次)
while count < 10:
num_list.append(a)
a, b = b, a + b # (1,0+1)
count += 1

# 遍历列表中的结果
for i in num_list:
print(i)

# 结果
0
1
1
2
3
5
8
13
21
34

使用迭代器

迭代器不会一次性将所有的元素都加载到内存中,而是需要使用的使用才返回结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Fibonacci(object):
"""斐波那契数列的迭代器"""
def __init__(self, all_num):
self.a = 0 # 第一位
self.b = 1 # 第二位
self.all_num = all_num # 需要运算的次数
self.current_num = 0 # 当前运算次数

def __iter__(self):
return self

def __next__(self):
if self.current_num < self.all_num:
result = self.a
self.a, self.b = self.b, self.a + self.b
self.current_num += 1
return result

else:
raise StopIteration

fibonacci = Fibonacci(10)
# 遍历生成器
for i in fibonacci:
print(i)


# 结果
0
1
1
2
3
5
8
13
21
34

内存对比 (不要在物理测试,系统直接卡死)

不使用迭代器运算100000000次时,系统被迫停止该进程,内存占用狂飙(总占用因卡顿未及时刷新)

image-20220213220728890

使用迭代器运算100000000次时,内存占比稳定在0.6左右,并且运行正常

image-20220213220537843

g)