如有兴趣了解更多请关注我的个人博客https://07xiaohei.com/
(三)迭代
1. 概述:
迭代是重复运行某个代码块的能力,在此处,特指使用for…in循环遍历某个可迭代对象的行为。
和其他使用下标完成迭代的语言不通,Python使用for…in循环完成迭代,这样做的好处是一些可以迭代的对象在没有下标的情况下也能够完成迭代——无论可迭代对象是否有序均可以完成迭代操作。
注意,迭代是取出元素本身而不是元素的索引。
2. 实例:
因为迭代本质上是用for循环的完成的,for循环的详细语法和内容请参考python基础——循环,这里只举例说明Python中可迭代对象的循环。
(1)list迭代:
-
基本迭代:
list_use = [0,1,2,3,4,5,6] for i in list_use: print(i,end=" ") # 运行结果: # 0 1 2 3 4 5 6
-
enumerate()函数迭代:
在迭代时,有时需要拿到索引,这时需要使用enumerate()函数。
enumerate()函数用于将一个可迭代对象组合为一个索引序列,同时列出其元素和元素下标。
语法格式:enumerate(sequence, [start=0])
sequence是迭代对象,start是下标起始位置的索引值,默认第一个元素开始,返回一个enumerate对象(本质上是列表+元组)。
enumerate()函数可以用于其他类型,后面会举例。
例子如下:
list_use = ['a','b','c','d','e','f'] for i in enumerate(list_use): print(i) for i,j in enumerate(list_use): print(str(i)+","+j) for i in enumerate(list_use): print(str(i[0])+","+i[1]) # 运行结果: # (0, 'a') # (1, 'b') # (2, 'c') # (3, 'd') # (4, 'e') # (5, 'f') # 0,a # 1,b # 2,c # 3,d # 4,e # 5,f # 0,a # 1,b # 2,c # 3,d # 4,e # 5,f
-
多个可迭代对象合并迭代:
此处需要使用zip()函数合并可迭代对象。
zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表对象。
返回的列表需要增加list()进行强制类型转化,其长度以最短的一个可迭代对象为准。
语法格式:zip([iterable, …]),iterable是可迭代对象。
例子如下:
list_score=[100,80,90,95,85] list_name=["jacklove","rookie","xiaotian","wayward","Mark"] for i in zip(list_score,list_name): print(i) for i,j in zip(list_score,list_name): print(str(i)+","+j) #运行结果: # (100, 'jacklove') # (80, 'rookie') # (90, 'xiaotian') # (95, 'wayward') # (85, 'Mark') # 100,jacklove # 80,rookie # 90,xiaotian # 95,wayward # 85,Mark
(2)tuple迭代:
tuple_use = (0,1,2,3,4,5,6)
for i in tuple_use:
print(i,end=" ")
# 运行结果:
# 0 1 2 3 4 5 6
tuple_use = ('a','b','c','d','e','f')
for i in enumerate(tuple_use):
print(i)
for i,j in enumerate(tuple_use):
print(str(i)+","+j)
for i in enumerate(tuple_use):
print(str(i[0])+","+i[1])
# # 运行结果:
# # (0, 'a')
# # (1, 'b')
# # (2, 'c')
# # (3, 'd')
# # (4, 'e')
# # (5, 'f')
# # 0,a
# # 1,b
# # 2,c
# # 3,d
# # 4,e
# # 5,f
# # 0,a
# # 1,b
# # 2,c
# # 3,d
# # 4,e
# # 5,f
(3)字符串迭代:
str_use = "0123456"
for i in str_use:
print(i,end=" ")
# 运行结果:
# 0 1 2 3 4 5 6
str_use = "abcdef"
for i in enumerate(str_use):
print(i)
for i,j in enumerate(str_use):
print(str(i)+","+j)
for i in enumerate(str_use):
print(str(i[0])+","+i[1])
# 运行结果:
# (0, 'a')
# (1, 'b')
# (2, 'c')
# (3, 'd')
# (4, 'e')
# (5, 'f')
# 0,a
# 1,b
# 2,c
# 3,d
# 4,e
# 5,f
# 0,a
# 1,b
# 2,c
# 3,d
# 4,e
# 5,f
(4)dict迭代:
dict的迭代可以是对key迭代,对value迭代,或者两者一起迭代,这些都被视为可迭代对象。
迭代key是默认迭代,也可以指定迭代dict的keys:
dirc_use = {1:'a',2:'b',3:'c',4:'d',5:'e'}
for i in dirc_use:
print(i,end=" ")
print()
for i in dirc_use.keys():
print(i, end=" ")
# 运行结果:
# 1 2 3 4 5
# 1 2 3 4 5
迭代values需要指定dirc的values:
dirc_use = {1:'a',2:'b',3:'c',4:'d',5:'e'}
for i in dirc_use.values():
print(i,end=" ")
# 运行结果:
# a b c d e
迭代两者需要指定dirc的items完成迭代:
dirc_use = {1:'a',2:'b',3:'c',4:'d',5:'e'}
for i in dirc_use.items():
print(i,end=" ")
# 运行结果:
# (1, 'a') (2, 'b') (3, 'c') (4, 'd') (5, 'e')
(四)迭代器
1. 容器
(1)概述:
容器是一种把多个元素组织在一起的数据结构,容器中的元素可以逐个迭代获取。
容器的最大特性是能够判断一个元素是否在这个容器内,一般使用in和not in来判断元素是否包含在容器中。
常用的list、tuple、dict、set、string都属于容器对象。
(2)底层逻辑:
容器能够使用in或not in判断是因为其实现了__contiains__方法,自定义容器时必须实现该方法(此处不详细阐述),所以,判断是否为容器,主要是判断其是否实现了__contiains__方法。
2. 可迭代对象iterable:
可迭代对象的逻辑和容器类似,需要根据其是否实现了__iter__方法来判断是否是可迭代对象。
即使__iter__方法没有具体实现,也符合对可迭代对象的定义,只是不能真的被迭代。
在表现上,所有可以用for循环遍历的对象都是可迭代对象,这样的对象能够迭代出容器的所有数据。
可迭代对象用__iter__方法能够返回一个迭代器,这个迭代器指向了可迭代对象的某个元素,更多的迭代器信息后文回继续介绍。
可迭代对象赋予了容器获得其中每一个元素的能力,但要注意有些容器并不是可迭代的。
3. 迭代器iterator:
当了解了容器和可迭代对象之后,就可以进一步了解迭代器了。
(1)概念:
迭代器的定义:一个对象同时实现了__iter__和__next__方法,该对象为迭代器。实现的这两个方法也被称为迭代器协议。
从中可以看出,迭代器一定是可迭代对象,因为其已经实现了__iter__方法。
如果只实现了一个方法,不足以作为迭代器使用。
(2)特征:
迭代器是一个带状态的对象,可以通过iter方法从可迭代对象处获得,通过next方法不断迭代,从而遍历所有元素,一旦没有可迭代元素,抛出StopIteration异常。
迭代器是从可迭代对象的第一个元素开始访问的,因此每次iter方法返回的迭代器指向元素都是唯一确定的,由于迭代器的next方法是返回下一个元素,因此迭代器只能往前不会后退,也因此迭代器是不能重复使用的,如果需要从头开始遍历,需要利用iter方法获得一个新的迭代器。
但是要注意,对于文件之类的可迭代对象,即使多次调用iter方法,实际上返回的每一个迭代器对应的文件指针所指向的内存地址是相同的,因为iter方法实际返回的正是可迭起对象本身,这是为了打开文件避免文件指针的跳转,同时保证每次打开只需要一次遍历获得文件内容,不需要多次遍历,除非多次打开。
for循环迭代的核心逻辑就是用iter方式获得可迭代对象的迭代器,反复使用next方法,直到捕获到Stopiteration异常,退出循环。
另外,通常使用的tuple、list、set等内置数据结构都是可迭代对象,而不是迭代器,迭代器是利用另外一个类实现的,也就是说,这些内置数据结构自身实现了iter方法,由另一个类实现了两个方法,从而将自身的迭代细节交给了另一个类实现。
(3)为什么要用迭代器而不是直接通过可迭代对象遍历:
迭代器能够延迟计算,节省内存空间,完成更广义的存储。
构建迭代器时不是把所有元素一次性加载到内存,而是通过延迟计算的方式返回元素,也就是只会在需要的时候计算下一个元素并返回,不使用时不会存储,这样将会使得使用的存储空间差距巨大,甚至完成一些不可能的存储工作。比如利用迭代器可以完成逻辑上的全部自然数存储,在需要时迭代到需要的数据使用next方法获得,用有限的空间完成了无限的存储(当然无限只是理论上的)。
(4)isinstance方法:
使用isinstance()可以快速判断一个对象是否为迭代器或者可迭代对象。
语法格式:isinstance(object, classinfo)
object是对象,classinfo是基本类型/直接类名/间接类名或者它们组成的元素。
如果对象类型和classinfo对应类型相同返回True,否则返回False。
使用该方法需要导入模块collections.abc
(5)实例:
首先判断一下各种基本类型是否是迭代器或者可迭代对象:
迭代器和可迭代对象需要导入模块collections.abc
from collections.abc import *
print(isinstance(1, Iterable))
#False
print(isinstance(2.3, Iterable))
#False
list_judge =[1,2,3,4,5]
tuple_judge =(1, 2, 3,4,5)
set_judge ={1,2,3,4,5}
str_judge ="12345"
dirc_judge={1:'a',2:'b'}
print(isinstance(list_judge, Iterable))
#True
print(isinstance(list_judge, Iterator))
#False
print(isinstance(tuple_judge, Iterable))
#True
print(isinstance(tuple_judge, Iterator))
#False
print(isinstance(set_judge, Iterable))
#True
print(isinstance(set_judge, Iterator))
#False
print(isinstance(str_judge, Iterable))
#True
print(isinstance(str_judge, Iterator))
#False
print(isinstance(dirc_judge, Iterable))
#True
print(isinstance(dirc_judge, Iterator))
#False
可以看到,字符串、元组、集合、字典和列表都是可迭代对象不是迭代器。
下面开始简单使用迭代器:
list_use = [1,2,3,4,5]
iter_use1 = list_use.__iter__()
iter_use2 = iter(list_use)
print(id(iter_use1))
print(id(iter_use2))
print(iter_use1)
print(iter_use2)
print(iter_use1.__next__())
print(iter_use1.__next__())
print(next(iter_use1))
print(next(iter_use1))
print(next(iter_use1))
print(next(iter_use1))
#运行结果:
# Traceback (most recent call last):
# File "D:\pycharmwork\blog_use.py", line 2224, in <module>
# print(next(iter_use1))
# ^^^^^^^^^^^^^^^
# StopIteration
# 2370164320256
# 2370164321504
# <list_iterator object at 0x00000227D8CD6800>
# <list_iterator object at 0x00000227D8CD6CE0>
# 1
# 2
# 3
# 4
# 5
下面开始使用迭代器配合for循环使用:
dict_use = {1:'a',2:'b',3:'c',4:'d',5:'e'}
iter_use=iter(dict_use)
for i in iter_use:
print(i,end=" ")
print()
iter_use=iter(dict_use.values())
for i in iter_use:
print(i,end=" ")
print()
iter_use=iter(dict_use.items())
for i in iter_use:
print(i,end=" ")
#运行结果:
# 1 2 3 4 5
# a b c d e
# (1, 'a') (2, 'b') (3, 'c') (4, 'd') (5, 'e')
下面使用迭代器,next函数配合while循环使用:
str_use ="abcdefg"
iter_use =iter(str_use)
while True:
try:
print(next(iter_use),end=" ")
except StopIteration:
print()
print("error,exit")
sys.exit()
# 运行结果:
# a b c d e f g
# error,exit
直接使用for循环遍历,实际上也是用迭代器完成的,只是被隐藏了:
list_use = [1,2,3,4,5]
for i in list_use:
print(i,end=" ")
# 1 2 3 4 5