循环
循环是指重复执行一段代码若干次,为什么要有循环?因为循环可以避免大量的代码重复。
死循环
当一个循环可以执行无限次,也就是没有终止条件,我们称这个循环是死循环。编写死循环程序实际上是不对的,一旦写出来一个程序运行后发现是死循环,一定要立即终止程序的运行!用Python实现一个死循环非常简单,while True就可以。
for实现死循环
步长为0
然而我的标题是要求用Python的for实现死循环,首先我们想到的是for i in range,range我之前的文章提到过,是在两个整数之间按照一定的步长生成一个序列,range是一个可迭代对象,而不是真的序列对象。那么有些人就想到了一个办法,步长为0不就是一直都不会改变值吗?这样不就是死循环了?事实上还是要通过程序运行来检测,简单的编写了一个程序,如图所示。
程序运行之后并没有执行成功,而是引发了一个异常,如图所示。
稍微翻译一下,第三个参数(步长)不能为0!这种办法直接宣告失败,还有其他办法!
终止条件是一个无限大的整数
Python里面表示无限大的数是使用float('inf')(无限大的小数),我只要再用int去转换一下不就是无限大的整数了吗?继续用代码实现一下,看看是不是真的能行。
步长没写,默认为1,运行之后还是异常,如图所示。
稍微翻译一下,不能把无限大的小数转换为整数!依旧是失败。。。。。
参考其他语言
Python的for i in range(start, end, step)写到Java里面就是for (int i = start; i < end; i += step)。根据之前的东西,Java实现的for死循环如下。
同样的逻辑,用到Python上也是非常简单,如图所示。
但是这样写运行出来并不是一个死循环,结果如图所示。
为什么会出现这样的结果?可能是因为for i in range的i和i = 0的i不是同一个i,是不是同一个可以通过使用id这个内置函数查看内存地址就行。不要只知其然,还要知其所以然!
为什么会选择从300开始循环?因为Python把一些常用的数据在运行之前都加到了内存中,这些数据就是从-5到255之间的所有整数,所以我选择避开这一段区域,选一段正常的地方来查看内存地址。运行结果如图所示。
从结果中我们也非常容易的看出来,因为地址不同,两个i并不是同一个i。
列表插入值
Python的for不仅仅可以用在range这里,还可以遍历容器,比如字符串,列表,元组,字典,集合……我们写死循环完全可以通过在for遍历列表的时候不停地插入值,让它一直遍历下去,如图所示。
这个程序确实是一个死循环,但是只是停留在理论上的死循环,无法让它真的在机器上无休止的运行,因为它在不停地开辟内存空间,总有一刻内存会爆!
for死循环(不会爆内存)
自定义可迭代对象
我之前讲过,要想让一个类实例化出来是一个可迭代对象,必须实现__iter__和__next__两个魔法方法,在这里完全可以对这两个方法动点手脚,实现无限迭代,如图所示。
itertools模块中的类
count
难道一定要自己定义类?难道没有相应的库给一个无限迭代的类吗?实际上无限迭代的东西Python确实有封装,实例化出来一个无穷迭代的对象的类确实有,大部分位于itertools模块,比如count,cycle,repeat……
count的构造方法有两个默认参数,第一个参数是开始计数(默认为0),第二个参数是步长(默认为1),调用这个构造方法(假设使用默认参数)会实例化出一个无穷迭代的对象,for迭代这个对象就会有如下输出。
0 1 2 3 4 5 ……它会无穷无尽的迭代下去,但是迭代到后来依旧会爆内存,因为Python没有数据越界,C语言里面都说整数int占用了四个字节,在Python里面就不是这样了,看下面一个例子,两个数同样是int类型,但是在内存中占用的字节数并不是一样的。
这里使用内置模块sys里面的getsizeof函数来查看对象占用多少个字节,可以发现0和10^50这两个整数占用的字节并不一样,总有一个无限大的整数会把内存给爆掉,具体多少我懒得算了。
cycle
cycle就是转圈,其构造方法传入一个可迭代对象,比如字符串"abc",for遍历这个对象,会一直循环输出a b c a b c……一直这样无限循环下去,而且不会爆内存。
repeat
repeat类实例化出来的对象也是无穷迭代的,它的构造方法必须传入一个参数,参数类型无限制,第二个参数是表示次数,默认是None,如果第二个参数保持默认,就产生了一个无穷迭代的对象,用for迭代会一直重复第一个参数。