迭代

重复获取下一个元素的过程,例如:循环获取容器中的元素

for原理:

  • 1.获取迭代器

    1
    iterator = message.__iter__()
  • 2.获取下一个元素

    1
    2
    3
    4
    5
    6
    7
    while True:
    try:
    item = iterator.__next__()
    print(item)
    # 3.如果停止迭代则跳出循环
    except StopIteration:
    break

可迭代对象(iterable)

可以创建迭代器的对象具有iter函数的对象,可以返回迭代器对象,例如列表,元组等容器

迭代器(iterator)

执行迭代过程的对象,可以被next()函数调用并返回下一个值的对象,例如for循环

案例:自定义迭代器的创建

分析1:

创建一个学生管理类

1
2
3
4
5
6
7
class StudentController:

def __init__(self):
self.__students = []

def add_student(self, stu):
self.__students.append(stu)

创建一个学生管理类的对象,并添加学生

1
2
3
4
controller = StudentController()
controller.add_student("张三")
controller.add_student("李四")
controller.add_student("王五")

如何遍历controller,若使用for循环,则会报错,因为’StudentController’ object is not iterable

1
2
for item in controller:
print(item) # 报错

分析2:为解决上述StudentController类不可迭代的问题,由文章开头for原理可知,要想可迭代需要具有iter函数,因此修改StudentController类,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class StudentController:

def __init__(self):
self.__students = []

def add_student(self, stu):
self.__students.append(stu)

def __iter__(self):
pass


controller = StudentController()
controller.add_student("张三")
controller.add_student("李四")
controller.add_student("王五")

for item in controller:
print(item)

上述代码报错,iter() returned non-iterator of type ‘NoneType’

分析3:由文章开头原理可知,iter函数应当返回一个迭代器对象。因此创建另外一个类StudentIterator:即学生迭代器;学生迭代器应当有next函数,且调用next方法时,应当返回下一个元素,当取完所有元素后,发起异常,结束迭代。学生迭代器的要获取的元素应当由可迭代对象给予。因此修改代码如下:完成要求

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
class StudentController:

def __init__(self):
self.__students = []

def add_student(self, stu):
self.__students.append(stu)

def __iter__(self):
return StudentIterator(self.__students)


class StudentIterator:
def __init__(self, data):
self.__data = data
self.__index = -1

def __next__(self):
if self.__index == len(self.__data) - 1:
raise StopIteration()
self.__index += 1
return self.__data[self.__index]


controller = StudentController()
controller.add_student("张三")
controller.add_student("李四")
controller.add_student("王五")

for item in controller:
print(item)

可迭代对象与迭代器为何分成两个类

因为多个可迭代对象可以使用同一个迭代器

生成器

前言

生成器:能够动态(循环一次计算一次返回一次)提供数据的可迭代对象

作用:在循环过程中,按照某种算法推算数据,不必创建容器存储完整的结果,从而节省内存空间。数据量越大,优势越明显。

生成器函数=可迭代对象+迭代器

生成器函数定义:含有yield语句函数,返回值为生成器对象

执行过程:

(1) 调用生成器函数会自动创建迭代器对象。

(2) 调用迭代器对象的next()方法时才执行生成器函数

(3) 每次执行到yield语句时返回数据,暂时离开。

(4) 待下次调用next()方法时继续从离开处继续执行。

引例

创建自定义Myrange类1.0版本

实现以下功能

1
2
for number in Myrange(5):
print(number) # 输出 0 1 2 3 4

代码如下,使用自定义迭代器实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class MyrangeIterator:
def __init__(self, target=None):
self.__target = target
self.__index = -1

def __next__(self):
self.__index += 1
if self.__index == self.__target:
raise StopIteration()
return self.__index


class Myrange:
def __init__(self, num=None):
self.__num = num

def __iter__(self):
return MyrangeIterator(self.__num)

# 循环一次 计算一次 返回一次
for number in Myrange(5):
print(number) # 输出 0 1 2 3 4

生成器函数yield

生成代码的大致规则:
1. 将yield关键字前面的代码作为__next__函数体
2. 将yield关键字后面的数据作为__next__返回值

创建自定义Myrange类2.0版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Myrange:
def __init__(self, num=None):
self.__num = num

def __iter__(self):
number = 0
yield number
number += 1
yield number
number += 1
yield number


mr = Myrange(3)
iterator = mr.__iter__()
while True:
try:
item = iterator.__next__()
print(item) # 0 1 2
except StopIteration:
break

分析:iterator = mr.iter()此处虽然调用的是iter函数,但是实际函数并不会进入iter函数体内部,而是直接进入while True,在执行item = iterator.next()时,进入iter()函数体内部,且将yield关键字前面的代码作为next函数体,将yield关键字后面的数据作为next返回值

优点:无需写迭代器类,但是iter内部代码不够灵活

创建自定义Myrange类3.0版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def my_range(stop):
number = 0
while number < stop:
yield number
number += 1


# 使用for
for num in my_range(5):
print(num)

# 使用for的原理
mr = my_range(5) # 调用但不执行函数体,而是返回生成器对象
iterator = mr.__iter__() # 此处调用iter函数,返回仍然是生成器对象,因此mr与iterator是相同的
while True:
try:
item = iterator.__next__()
print(item)
except StopIteration:
break

找出列表中为偶数的元素

1
2
3
4
5
6
7
8
9
10
11
12
list01 = [43, 43, 54, 56, 76, 87, 98]


def get_evens(list_target):
for item in list_target:
if item % 2 == 0:
yield item


result = get_evens(list01)
for item in result:
print(item)

内置生成器

enumerate

语法:读写数据时使用 快捷键:itere+回车

1
2
3
list = [1, 2, 3, 4, 5]
for i, item in enumerate(list):
print(i, item)

zip

语法

1
2
3
4
list_name = ['张三', '李四', '王五']
list_age = [18, 19, 20]
for item in zip(list_name, list_age):
print(item)

输出

1
2
3
('张三', 18)
('李四', 19)
('王五', 20)