可迭代对象
可以用for循环的都是可迭代对象,那么为什么有的对象可以用for循环,而有的确不行呢?
list
tuple
dict
set
range()
enumerate()
....
这些对象都可以用for循环取得想要的值。
ls = dir(list)
tu = dir(tuple)
di = dir(dict)
se = dir(set)
ra = dir(range(10))
en = dir(enumerate([]))
#使用dir获得参数的属性,方法列表
s = set(ls) & set(tu) & set(di) & set(se) & set(ra) & set(en)
#取交集
print(s)
在这些交集中有一个属性为__iter__。也正是因为这个属性,它们才可以被迭代,也就是用for循环。
综上,当一个对象拥有__iter__方法时,它就是一个可迭代对象,也就可以用for循环。
迭代器
print(type([].__iter__()))
我们对列表使用一下__iter__()方法,查看它的类型发现它变成了iterator,也就是迭代器
print(dir([].__iter__()))
ls = [1,2,3]
iterator = ls.__iter__()
print(iterator.__next__())
print(iterator.__next__())
print(iterator.__next__())
在iterator的众多方法中有一个叫做__next__()的方法,使用它可以依次得到iterator中的值。
同样它也有一个__iter__()方法,说明可以对iterator进行for循环
ls = [1,2,3]
iterator = ls.__iter__()
for i in iterator:
print(i)
当然我们也可以使用while
ls = [1,2,3]
iterator = ls.__iter__()
while True:
print(iterator.__next__())
如果用while True将会一直取下一个元素,但如果没有下一个元素了怎么办,这时就会抛出一个StopIterator的异常,表示已经取到最后一个了,不能再往下继续了。
现在来说说for的工作原理,如果给它的不是一个可迭代对象,它就直接报错,如果给它一个可迭代对象像list...它会先调用内置的__iter__()方法得到该对象的迭代器对象,然后调用迭代器对象的__next__()方法,一直依次向下取得迭代器对象中的每一个元素,直到迭代器没有下一个元素了。如果给它的直接是一个迭代器对象,那么就直接调用__next__()方法
Tips:
- 如果一个对象有__iter__()方法,那么它就是一个可迭代对象
- 如果一个对象既有__iter__()方法,又有__next__()方法,那么它就是一个迭代器对象
- 由以上两点,迭代器一定是可迭代对象,但可迭代对象不一定是迭代器
- 一个可迭代对象调用它的__iter__()方法,就可以返回一个迭代器对象
- 调用迭代器的__next__()方法,可以一个一个地获取值。
isinstance
如果我们要判断一个对象是不是一个可迭代对象或者是一个迭代器对象的时候,我们去查看dir,一个一个找它们是不是有__iter__()或是__next__(),实在太low,这时候我们使用isinstance()函数即可判断。
from collections import Iterable,Iterator
print(isinstance([],Iterable))
print(isinstance([],Iterator))
print(isinstance({1,2,3},Iterable))
xrange和range
现在讨论这个问题可能已经没有多大意义了,因为在Python3.x中xrange和range被统一为range
- 在Python2.x中使用range()是生成一个序列
- 在Python2.x中使用xrange()是生成一个迭代器,要一个值取一个,不需要一次性开辟一块很大的内存空间。因此在性能上xrange会比range更优。
在Python3.x中xrange被range取代,也就是说只有range存在,并且生成的是一个迭代器。