"But be on your guard so that your hearts are not wighted down with dissipation and drunkenness and the worries of this life, and that day close down upon you suddenly like a trap. For it will overtake all who live on the face of the whole earht. But staty alert at all times, praying that you may have strength to eacape all these things that must happen, and to stand before the Son of Man."

你们要谨慎,恐怕因贪食醉酒并今生的思虑,累住你们的心,那日子就如同网罗忽然临到你们。因为那日子要这样临到全地上一切居住的人。你们要时时儆醒,常常祈求,是你们能逃避这一切要来的事,得以站立在人子面前。(LUKE 21:34-36)

语句(3)

循环,你我都在其中。

日月更迭,斗转星移,无不是循环;王朝更迭,子子孙孙,从某个角度看也都是循环。

循环是现实生活中常见的现象。

编程语言就是要解决现实问题的,因此也少不了要循环。

for循环

在高级编程语言中,大多数都有for循环(for loop),关于这种循环,我借用维基百科中“for loop”的说明,帮助大家理解。

In computer science a for-loop (or simply for loop) is a programming language control statement for specifying iteration, which allows code to be executed repeatedly. The syntax of a for-loop is based on the heritage of the language and the prior programming languages it borrowed from, so programming languages that are descendants of or offshoots of a language that originally provided an iterator will often use the same keyword to name an iterator, e.g., descendants of ALGOL use "for", while descendants of Fortran use "do." There are other possibilities, for example COBOL which uses "PERFORM VARYING".

如果看上面的英文有点难度,可以看下面的翻译,当然是很烂的翻译,绝对达不到“信雅达”的境界,虽然一直追求“德艺双馨”。

在计算机科学中for循环是编程语言中针对可迭代对象的语句,它允许代码被重复执行。for循环的语法是在对历史上的编程语言继承和借鉴基础上形成的,该语言原来有迭代器,则后来的编程语言也用同样的关键词来实现迭代,比如ALGOL系的使用“for”,而Fortan系的使用“do”,也有例外,如COBOL用“PERFORM VARYING”。

for循环是从ALGOL那里继承过来的。ALGOL(ALGOrithmic Language)是最早的高级编程语言(几乎没有之一),后来的不少高级语言都继承了ALGOL的某些特性,比如Pascal、Ada、C语言等,也包括Python。

维基百科上对此有非常清晰的说明。

The name for-loop comes from the English word for, which is used as the keyword in most programming languages to introduce a for-loop. The term in English dates to ALGOL 58 and was popularized in the influential later ALGOL 60; it is the direct translation of the earlier German für, used in Superplan (1949–1951) by Heinz Rutishauser, who also was involved in defining ALGOL 58 and ALGOL 60. The loop body is executed "for" the given values of the loop variable, though this is more explicit in the ALGOL version of the statement, in which a list of possible values and/or increments can be specified.

在ALGOL中,循环的关键词就是用for。所以,Python要继承这个光荣传统,也用for来实现循环。

于是有了:for语句。

其基本结构是:

for 循环规则:
    操作语句

从这个基本结构看,有着同if条件语句类似的地方:都有冒号;语句块都要四个空格缩进。

从例子中理解for循环

下面这个例子似曾相识。

>>> hello = "world"
>>> for i in hello:
...     print i        #Python 3: print(i)
... 
w
o
r
l
d

这个例子中实现的就是for循环。下面庖丁解牛:

  1. hello = "world",赋值语句,实现变量hello和字符串"world"之间的引用关系。
  2. for i in hello:for是发起循环的关键词;i in hello是循环规则,字符串类型的对象是序列类型,能够从左到右一个一个地按照索引读出每个字符,于是变量i就按照索引顺序,从第一字符开始,依次获得该字符的引用。
  3. i="w"的时候,执行print i或者print(i),打印出了字母w;然后循环第二次,让 i="e",执行print i或者print(i),打印出字母e;……,如此循环下去,一直到最后一个字符被打印出来,循环自动结束。注意,每次打印之后,要换行。如果不想换行,怎么办?参见《语句(1)》中相关内容。

因为可以也通过使用索引,得到序列对象的某个元素。所以,还可以通过下面的循环方式实现同样效果:

>>> for i in range(len(hello)):
...     print hello[i]        #Python 3: print(hello[i])
... 
w
o
r
l
d

其工作方式是:

  1. len(hello)得到hello引用的字符串的长度,为5
  2. range(len(hello),就是range(5),也就是[0, 1, 2, 3, 4],对应这"world"每个字母索引。这里应用了一个新的函数range(),关于它的用法,继续阅读,就能看到了。
  3. for i in range(len(hello)),就相当于for i in [0,1,2,3,4],让i依次得到列表中的各个值。当i=0时,打印hello[0],也就是第一个字符。然后顺序循环下去,直到最后一个i=4为止。

以上的循环举例中,显示了对字符串的字符依次获取,也涉及了列表,感觉不过瘾呀。

那好,看下面对列表的循环:

>>> ls_line = ['Hello', 'I am qiwsir', 'Welcome you', '']

>>> for word in ls_line:
...     print word        #Python 3: print(word)
... 
Hello
I am qiwsir
Welcome you

>>> for i in range(len(ls_line)):
...     print ls_line[i]        #Python 3: print(ls_line[i])
... 
Hello
I am qiwsir
Welcome you

以上两个例子中,是将for循环用于字符串和列表,此外,还可以用于字典、元组。例如:

>>> d = dict([("website", "www.itdiffer.com"), ("lang", "python"), ("author", "laoqi")])
>>> d
{'website': 'www.itdiffer.com', 'lang': 'python', 'author': 'laoqi'}
>>> for k in d:
    print k    #Python 3: print(k)

输出结果是:

website
lang
author

注意到,上面的循环,其实是读取了字典的key。在字典中,有一个方法,dict.keys(),得到的是字典key列表。

>>> for k in d.keys():
    print k    #Python 3: print(k)

website
lang
author

这种循环方法和上面的循环方法,结果是一样的,但是,这种方法并不提倡,以为它在执行速度上表现欠佳。

如果要获得字典的value怎么办?不要忘记dict.values()方法。读者可以自行测试一番。

除了可以单独获得key或者value的循环之外,还可以这么做:

>>> for k, v in d.items():
    print k + "-->" + v        #Python 3: print(k+"-->"+v)

website-->www.itdiffer.com
lang-->python
author-->laoqi

在工程实践中,你一定会遇到非常大的字典。用上面的方法,要把所有的内容都读入内存,内存东西多了,可能会出麻烦。为此,Python中提供了另外的方法。

>>> for k,v in d.iteritems():   #注意:仅在Python2中可用,Python 3中已经做了优化,d.item()即有同等功能。
    print k + "-->" + v

website-->www.itdiffer.com
lang-->python
author-->laoqi

这里是循环一个迭代器,迭代器在循环中有很多优势。除了刚才的dict.iteritems()之外,还有dict.itervalues()dict.iterkeys()供你选用(以上三个dict.iter*都只用在Python 2中,Python 3中已经不需要了)。

>>> d.iteritems()       #注意:仅在Python2中可用。
<dictionary-itemiterator object at 0x000000000322E368>

至于对元组的循环,读者自行尝试即可。

除了上述的对象之外,for循环还能应用到哪些对象上?能不能用在数字上呢?

>>> for i in 321:
    print i

Traceback (most recent call last):
  File "<pyshell#48>", line 1, in <module>
    for i in 321:
TypeError: 'int' object is not iterable

报错了。这说明对于数字不能使用for循环。不过,光知道这个还不行,还要看看报错信息。

报错信息中告诉我们,int对象不是可迭代的。言外之意是什么?那就是for循环所应用的对象,应该是可迭代的。

那么,怎么判断一个对象是不是可迭代的呢?

>>> import collections

引入collections这个标准库。要判断数字321是不是可迭代的,可以这么做:

>>> isinstance(321, collections.Iterable)
False

返回了False,说明321这个整数类型的对象,是不可迭代的。再判断一个列表对象。

>>> isinstance([1,2,3], collections.Iterable)
True

从返回结果,我们知道,列表[1,2,3]是可迭代的。

当然,并不是要你在使用for循环之前,非要判断某个对象是否可迭代。因为至此,你已经知道了字符串str、列表list、字典dict、元组tuple、集合set都是可迭代的。

range(start,stop[, step])

这个内建函数,非常有必要给予说明,因为它会经常被使用。一般形式是range(start, stop[, step])

要研究清楚一些函数特别是内置函数的功能,建议首先要明白内置函数名称的含义。因为在Python中,名称不是随便取的,是代表一定意义的。所谓:名不正言不顺。

range

n. 范围;幅度;排;山脉 vi. (在...内)变动;平行,列为一行;延伸;漫游;射程达到 vt. 漫游;放牧;使并列;归类于;来回走动

在具体实验之前,还是按照管理,摘抄一段官方文档的原话,让我们能够深刻理解之:

This is a versatile function to create lists containing arithmetic progressions. It is most often used in for loops. The arguments must be plain integers. If the step argument is omitted, it defaults to 1. If the start argument is omitted, it defaults to 0. The full form returns a list of plain integers [start, start + step, start + 2 step, ...]. If step is positive, the last element is the largest start + i step less than stop; if step is negative, the last element is the smallest start + i * step greater than stop. step must not be zero (or else ValueError is raised).

从这段话,我们可以得出关于range()函数的以下几点:

  • 这个函数可以创建一个数字元素组成的列表。
  • 这个函数最常用于for循环(关于for循环,马上就要涉及到了)
  • 函数的参数必须是整数,默认从0开始。返回值是类似[start, start + step, start + 2*step, ...]的列表。
  • step默认值是1。如果不写,就是按照此值。
  • 如果step是正数,返回列表的最后的值不包含stop值,即start+istep这个值小于stop;如果step是负数,start+istep的值大于stop。
  • step不能等于零,如果等于零,就报错。

在实验开始之前,再解释range(start,stop[,step])的含义:

  • start:开始数值,默认为0,也就是如果不写这项,就是认为start=0
  • stop:结束的数值,必须要写的。
  • step:变化的步长,默认是1,也就是不写,就是认为步长为1。坚决不能为0

实验开始,请以各项对照前面的讲述:

Python 2:

>>> range(9)                #stop=9,别的都没有写,含义就是range(0,9,1)
[0, 1, 2, 3, 4, 5, 6, 7, 8] #从0开始,步长为1,增加,直到小于9的那个数 
>>> range(0, 9)
[0, 1, 2, 3, 4, 5, 6, 7, 8]
>>> range(0, 9, 1)
[0, 1, 2, 3, 4, 5, 6, 7, 8]

>>> range(1, 9)              #start=1
[1, 2, 3, 4, 5, 6, 7, 8]

>>> range(0, 9, 2)            #step=2,每个元素等于start+i*step,
[0, 2, 4, 6, 8]

Python 3:

>>> range(9)
range(0, 9)
>>> list(range(9))
[0, 1, 2, 3, 4, 5, 6, 7, 8]

仅仅解释一下range(0, 9, 2)

  • 如果是从0开始,步长为1,可以写成range(9)的样子,但是,如果步长为2,写成range(9, 2)的样子,计算机就有点糊涂了,它会认为start=9, stop=2。所以,在步长不为1的时候,切忌,要把start的值也写上。
  • start=0, step=2, stop=9,列表中的第一个值是start=0, 第二个值是start+1step=2(注意,这里是1,不是2,不要忘记,前面已经讲过,不论是列表还是字符串,对元素进行编号的时候,都是从0开始的),第n个值就是start+(n-1)step。直到小于stop前的那个值。

熟悉了上面的计算过程,看看下面的输入谁是什么结果?

>>> range(-9)

我本来期望给我返回[0,-1,-2,-3,-4,-5,-6,-7,-8],我的期望能实现吗?

分析一下,这里start=0, step=1, stop=-9.

第一个值是0;第二个是start+1*step,将上面的数代入,应该是1,但是最后一个还是-9,显然出现问题了。但是,Python在这里不报错,它返回的结果是:

Python 2:

>>> range(-9)  
[]
>>> range(0,-9)
[]
>>> range(0)
[]

Python 3:

>>> range(-9)
range(0, -9)
>>> list(range(-9))
[]

报错和返回结果,是两个含义,虽然返回的不是我们要的。应该如何修改呢?

Python 2:

>>> range(0, -9, -1)
[0, -1, -2, -3, -4, -5, -6, -7, -8]
>>> range(0, -9, -2)
[0, -2, -4, -6, -8]

Python 3:

>>> range(0, -9, -1)
range(0, -9, -1)
>>> list(range(0, -9, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8]

有了这个内置函数,很多事情就简单了。比如:

>>> range(0, 101, 2)        #Python 2。如果用Python 3,类似前述演示
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]

100以内的自然数中的偶数组成的列表,就非常简单地搞定了。

思考一个问题,现在有一个列表,比如是["I","am","a","pythoner","I","am","learning","it","with","qiwsir"],要得到这个列表的所有序号组成的列表,但是不能一个一个用手指头来数。怎么办?

请沉思两分钟之后,自己实验一下,然后看下面。

>>> pythoner
['I', 'am', 'a', 'pythoner', 'I', 'am', 'learning', 'it', 'with', 'qiwsir']
>>> py_index = range(len(pythoner))     #以len(pythoner)为stop的值
>>> py_index                                                    #Python 3: list(py_index)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

再用手指头指着列表里面的元素,数一数,是不是跟结果一样。

例:找出小于100的能够被3整除的正整数。

分析:这个问题有两个限制条件,第一是小于100的正整数,根据前面所学,可以用range(1,100)来实现;第二个是要解决被3整除的问题,假设某个正整数n,这个数如果能够被3整除,也就是n % 3 == 0。那么如何得到n呢,就是要用for循环。

以上做了简单分析,要实现流程,还需要细化一下。按照前面曾经讲授过的一种方法,要画出问题解决的流程图。

下面写代码就是按图索骥了。

代码:

#! /usr/bin/env python
#coding:utf-8

aliquot = []

for n in range(1,100):
    if n % 3 == 0:
        aliquot.append(n)

print aliquot        #Python 3:print(aliquot)

代码运行结果:

[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]

上面的代码中,将for循环和if条件判断都用上了。

不过,感觉有点麻烦,其实这么做就可以了:

>>> range(3, 100, 3)
[3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 93, 96, 99]

for循环在Python中应用广泛,所以,下节还要深入研究这个语句的使用,不要小看它,虽然简单,但内涵深刻。


总目录   |   上节:语句(2)   |   下节:语句(4)

如果你认为有必要打赏我,请通过支付宝:[email protected],不胜感激。

results matching ""

    No results matching ""