在前面学习讲完while循环之后,现在终于要将for循环这个坑填上了。之所以拖到现在是因为for循环对前面讲过的序列、字典、集合都是有效的,讲完前面的内容再来讲for循环会更加容易上手。
首先,for循环和while循环一样,都是在满足一定条件的时候对其内层的代码进行循环执行。不同的是,while循环判断的是条件,而for判断的是迭代对象。
我们先来看for循环的代码:
a = (1, 2, 3, 4, 5) for x in a: print x
我们以序列中的元祖为例,发现其输出了这些,那么这段代码的逻辑是怎么样的?为了方便大家理解,我画了这样一个图:
我写了一个收租的小故事,方便大家理解。不知道为什么写的时候感觉眼角有点湿润。
for循环其实就是不断的去可迭代对象中拿去元素,而可迭代对象在每次迭代的时候都会把指针下移一格,也就是下次再来拿的时候,拿的是下一个。而这是因为可迭代对象有这样行为,才称其为可迭代。
这个时候我又要问一个问题,在迭代循环结束以后,x的值是否还存在?
当然还在,都说了作用域是函数的东西,迭代循环并不是函数。按照for循环的思路,x的值是不断更新的,所以在循环结束的时候,x应该等于最后一次迭代的值。以这里为例,x在循环结束的时候,其值应该为5。即x=5。
a = (1, 2, 3, 4, 5) for x in a: print x print ‘----‘,x print a
另外,虽说交租是交了出去自己没有了,但是迭代循环并不会改变a本身,也就是相当于借给别人看一眼,东西还是自己的。
当然现实中没有这样的福利就是了。
序列的迭代都和上面的一样,其都是按照索引的顺序依次给出。而字典和集合都是无序的,所以循环得到的顺序和我们代码写的顺序是不同的。但是我在字典篇中说过,字典的无序体现在其保存上,也就是一旦保存以后,其顺序虽然和我们代码写的顺序不同,但也不会每次循环得到的顺序都不同。如果每次循环得到的顺序都不同那要多大工程,浪费多少计算资源,这显然不符合python化繁为简的哲学。
另外,这里提醒一句,字典循环得到的键,而集合没有键的概念,所以得到的元素。
1.迭代器和生成器
你可能会看到这样的写法:
for x in range(1,5): print x
range()是什么鬼,我们先进交换模式看一下先:
直接返回了一个列表,也就是这个函数是快速生产列表的咯,for循环就相当于迭代了列表了咯,这就好理解了。
还可能会有这个写法:
for x in xrange(1,5): print x
xrange()又是什么鬼?
返回了自己。
好吧,这里解释一下,什么是迭代器和生成器了。
1.迭代器
迭代器是一个实现了迭代器协议的对象,Python中的迭代器协议就是有next方法的对象会前进到下一结果,而在一系列结果的末尾是,则会引发StopIteration。
而在for循环中,会自动调用 iter()将我们要迭代的对象转化为可迭代对象,每次循环都会调用 .next() 方法获取新元素,当引发StopIteration错误的时候自动退出循环,这就for循环的内部操作。
常用的数据类型,如:str、tuple、list、dict、set,都能进行迭代循环,因为其内部都有相应的方法,如list中的:
所以我们自己也可以创建一个可迭代的类:
class Text(): def __init__(self,list_input): #初始化函数 self.list = list_input self.i = 0 def __iter__(self): return self def next(self): if self.i == len(self.list): #如果索引到了最后,说明迭代完毕 self.i = 0 #将索引归0 raise StopIteration #触发错误 #如果索引没到最后 self.i += 1 #索引先后移一位 return self.list[self.i - 1] #取出前一位的值
a = Text([1,2,3]) for x in a: print x
这就是迭代器了。
2.生成器
xrange()就是生成器。
所谓的生成器就是每次调用的时候返回一个对象,而不是一次性在内存中创建,从而达到节约内存的作用。
而生成器靠yield关键字实现,生成器的编写类似于函数,只不过将函数的return改成了yield:
def scq(): yield 1 yield 2 yield 3 a = scq() print a.next() print a.next() print a.next()
每次调用a.next()的时候得到的都是不同的值。
当然我们也可以像函数一样处理它:
总之,yield的核心在于冻结函数,一旦遇到,冻结这个函数;而return在于结束函数,一旦遇到,返回结果,函数整个退出,下次调用时重新开始执行。
当然,到了最后一个的时候也会触发错误:
可以看出,生成器是每次调用的时候生成一个对象,所以生成器比迭代器更节约内存,但也更耗费cpu,因为代码需要运算。不过一般情况下,使用生成器会有更高的效率。
最后补充range和xrange两个函数的用法:
range和xrange都接受三个参数:
其中start和stop表示开始和结束,同样不包括结束的那个值,后面的只是个分界线而已。当只给一个参数的时候,默认从0开始,即start=0。
而step表示步长,和序列中的一样,表示走几步执行生成一次。
暂时先写这么多,后面有什么错误和补充的会继续完善。
参考和转载的文献:戳这里