for循环是如何工作的
当我们拥有一个列表 l = [1,2,3,4,5]
,想取列表中的内容,有几种方式?
1 | # 1 通过索引下标和切片取值 |
1 | # 2 通过for循环取值 |
他们的区别是,使用索引取值可以取到任意位置的值,前提是我知道这个值在什么位置,而for循环是取到每一个值,不需要关心这个值在什么位置,也不能跳过任何一个值去取其他位置的值,我们可以称作循环遍历。那么for循环到底是怎么工作的呢?
都有哪些数据类型可以被for循环
1 | for s in 'abcde': |
当我们循环数字类型的时候报错了,说int类型不是 iterable(可迭代的)
迭代和可迭代协议
通过对数字类型的报错,不可被for循环的数据类型会报错 不是一个可迭代的,那么是不是说可迭代的数据类型就可以被for循环,如何判断数据类型是否可以被迭代?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from collections import Iterable
l = [1,2,3,4]
t = (1,2,3,4)
d = {1:2,3:4}
s = {1,2,3,4}
num = 123
money = 10.10
print(isinstance(l,Iterable)) # True
print(isinstance(t,Iterable)) # True
print(isinstance(d,Iterable)) # True
print(isinstance(s,Iterable)) # True
print(isinstance(num,Iterable)) # False
print(isinstance(money,Iterable)) # False
# 下面这三种也是可以被循环遍历
# f = open()
# range()
# enumerate 枚举
可以将某个数据集内的数据“一个挨着一个的取出来”,就叫做迭代
总结出一条规律来:能被for循环的就是“可迭代的”。
但是如果正着想,for怎么知道谁是可迭代的呢?为什么能被for循环?
1 | # 为什么能够被循环 |
再总结出一条新的规律: 能被for循环的就是“可迭代的”,只要是能被for循环的数据类型,就一定拥有__iter__
双下方法
双下方法__iter__
做了什么
1 | print([].__iter__()) # <list_iterator object at 0x0000000002308940> 迭代器 iterator |
- 通过上面的例子我们发现,当一个可迭代的对象调用了iter()方法会生成一个 iterator (迭代器)
- 迭代器中含有
_next__()
方法,他可以一个一个的取值,如果我们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉我们,列表中已经没有有效的元素了
可迭代协议 与 迭代器协议
根据上面的例子我们总结出以下概念:
- 能被for循环的数据类型都是 可迭代的 (iterable)
- 当这个数据类型调用
.__iter__()
方法会生成一个 迭代器(iterator) - 迭代器.next()可以一个一个的取值
- for循环其实就是在使用迭代器,只有是可迭代对象或者迭代器,才能用for循环
- for循环的本质就是迭代器
1 | for i in l: |
1 | # 模拟for循环 |
可迭代协议: 只要含有__iter__()
方法的都是可迭代的
迭代器协议: 内部含有__next__()
方法和__iter__()
方法的就是迭代器
可迭代的不一定就是迭代器
- 迭代器:内部有
__iter__
和__next__
方法 ,所以他一定是可迭代的 - 可迭代的不一定是迭代器,要看有没有
__next__
方法1
2
3
4
5
6
7
8
9
10from collections import Iterable
from collections import Iterator
print(isinstance([],Iterable)) # 可迭代的 # True
print(isinstance([],Iterator)) # 迭代器 # False ,list是可迭代的,但不是一个迭代器
print('__iter__' in dir(range(12))) # True
print('__next__' in dir(range(12))) # False
print(isinstance(range(100000000),Iterable)) # True
print(isinstance(range(100000000),Iterator)) # False , range是可迭代器,但不是一个迭代器,因为它没有__next__()方法
迭代器的好处
- 迭代器会从容器类型中 一个一个的取值,会把所有的值都取到。
- 它可以节省内存空间,迭代器并不会在内存中再占用一大块内存,而是随着循环每次生成一个,或者每次next()每次给我一个
1
2# print(range(10000000)) # 很快,但是并不会在内存中真正的生成数据
# print(list(range(10000000))) # 强制转列表会导致崩溃,list是真正存在并存储在内存里 ,range是要一个给一个