迭代(iteration)
如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration
)
在Python中,迭代是通过for ... in
来完成的,所以,当我们使用for循环时,只要作用于一个可迭代对象,for循环就可以正常运行,而我们不太关心该对象究竟是list还是其他数据类型。
因为
dict
的存储不是按照list的方式顺序排列,所以,迭代出的结果顺序很可能不一样。
默认情况下,dict迭代的是key。如果要迭代value,可以用for value in d.values()
,如果要同时迭代key和value,可以用for k, v in d.items()
。
可以通过collections
模块的Iterable
类型判断一个对象是否为可迭代对象:
>>> from collections import Iterable
>>> isinstance('abc',Iterable)
True
>>> isinstance(123,Iterable) #int类型不可迭代
False
>>> isinstance([1,2,3],Iterable)
True
任何可迭代对象都可以作用于for循环,包括我们自定义的数据类型,只要符合迭代条件,就可以使用for循环。
三元运算与列表解析
三元运算:在赋值变量的时候,可以直接加判断,然后赋值
if 1>3:
temp = 'gt'
else:
temp = 'lt'
print(temp)
result = 'gt' if 1>3 else 'lt'
print(result)
列表解析
# user = []
# for i in range(10):
# user.append('user%s' %i)
# print(user)
user = ['user%s' %i for i in range(10)]
print(user) #['user0', 'user1', 'user2', 'user3', 'user4', 'user5', 'user6', 'user7', 'user8', 'user9']
user1 = ['user%s' %i for i in range(10) if i>5]
print(user1) #['user6', 'user7', 'user8', 'user9']
迭代器(iterator)
可迭代对象(iterable):在python中list、tuple、dict、set、str
等数据类型可以直接作用于for循环,这些可以直接作用于for循环的对象统称为可迭代对象(Iterable)
。(实现:对象内部定义一个__iter__
方法)
迭代器(iterator):可以被next()
函数调用并不断返回下一个值的对象称为迭代器(Iterator)
。
迭代器协议:指对象必须提供一个next
方法,执行该方法要么返回迭代中的下一项,要么就引起一个Stoplteration
异常,以终止迭代(只能往后走不能往前退)
协议是一种约定,可迭代对象实现了迭代器协议,python的内部工具(如
for,sum,min,max
函数等)使用迭代器协议访问对象
可以使用isinstance()
判断一个对象是否是Iterator
对象:
>>> from collections import Iterator
>>> isinstance('abc',Iterator)
False
>>> isinstance((i for i in range(10)),Iterator) #生成器都是Iterator对象
True
字符串、列表、元组、字典、集合、文件对象这些都不是可迭代对象,只不过在for循环时调用了他们内部的__iter__
方法,把他们变成了可迭代对象。然后for循环调用可迭代对象的__next__
方法取值,而for循环会捕捉Stoplteration
异常,以终止迭代
>>> isinstance(iter('abc'),Iterator)
True
>>> l = [1,2,3]
>>> iter_l = l.__iter__()
>>> print(iter_l.__next__())
1
>>> print(iter_l.__next__())
2
>>> print(iter_l.__next__())
3
>>> print(iter_l.__next__())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
总结:
- 凡是可作用于
for
循环的对象都是Iterable
类型; - 凡是可作用于
next()
函数的对象都是Iterator
类型,它们表示一个惰性计算的序列; - 集合数据类型如
list、dict、str
等是Iterable
但不是Iterator
,不过可以通过iter()
函数获得一个Iterator
对象。 - Python的
for
循环本质上就是通过不断调用next()
函数实现的,例如:
l = [1,2,3]
# for i in l:
# print(i) #for循环完全等价于如下内容
iter_l = l.__iter__()
while True:
try:
print(iter_l.__next__())
except StopIteration:
print('迭代完毕,循环终止')
break
生成器(generator)
通过列表解析,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(generator)
。
生成器可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__
方法),所以生成器就是可迭代对象。
生成器分类及在python中的表现形式:(python有两种不同的方式提供生成器)
- 生成器函数:常规函数定义,但是,使用
yield
语句而不是return
语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次重它离开的地方继续执行 - 生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表
生成器表达式:
#列表解析
user = ['user%s' %i for i in range(10)]
#生成器表达式
user = ('user%s' %i for i in range(10))
user_scq = ('user%s' %i for i in range(10))
print(user_scq) #<generator object <genexpr> at 0x0000000001DEEF10>
print(next(user_scq)) #user0
print(user_scq.__next__()) #user1
把列表解析的[]
换成()
得到的就是生成器表达式;列表解析和生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存
生成器函数:
def test():
for i in range(10):
yield i
res = test()
print(res) #<generator object test at 0x0000000001DEEF10>
print(next(res)) #0
print(res.__next__()) #1
生成器总结:
- 语法上和函数类似:生成器函数和常规函数几乎是一样的。他们都是使用
def
语句进行定义,差别在于生成器使用yield
语句返回一个值,而常规函数使用return
函数返回一个值 - 自动实现迭代协议:对于生成器,python会自动实现迭代器协议,以便应用到迭代背景中(如for循环,sum函数)。由于生成器自动实现了迭代协议,所以我们可以调用它的
next
方法,并且在没有值可以返回的时候,生成器自动产生Stoplterration
异常 - 挂起状态:生成器使用
yield
语句返回一个值。yield
语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行。
优点:
1.生成器的好处是延迟计算,一次返回一个结果。也就是说,它不会一次生成所有的结果,这对于大数据处理将会非常有用
#列表解析
sum([i for i in range(10000000000)]) #内存占用大,机器容易卡死
#生成器表达式
sum(i for i in range(10000000000)) #几乎不占内存
2.生成器还能有效提高代码可读性
注意事项:生成器只能遍历一次
练习:读取文件,计算出各省人口占总人口的百分比
#111文件
{'name':'北京','population':10000}
{'name':'河北','population':30000}
{'name':'山东','population':15000}
{'name':'山西','population':24000}
def get_population():
with open('111','r',encoding='utf-8') as f:
for i in f:
yield i
res = get_population()
all_population = sum(eval(i)['population'] for i in res)
print(all_population)
for i in get_population():
i_dic = eval(i)
print(i_dic['name'],'%s %%'%(i_dic['population'] / all_population * 100))