14 Python条件、循环和其他语句

import的使用

语言的学习只有在不断深入后才能进一步发现其中隐藏的惊人特性,即使是简单的print函数,在不断使用后也会发现更多使用方式,import将带你进入一个更快捷的编程模式。
1 import语句
在讲解import语句之前先看一个示例:

import math
r=5
print('半径为5 的圆的面积为:%.2f' %(math.pi*r**2))

保存文件名为import_test.py。在cmd命令窗口执行如下命令:

D:\python\workspace>python import_test.py
半径为5 的圆的面积为:78.54

上面的程序使用了import语句。
import math的意思为从Python标准库中引入math.py模块,这是Python中定义的引入模块的方法。import的标准语法如下:

import module1[, module2[,... moduleN]]

表示允许一个import导入多个模块,但各个模块间需要用逗号隔开。
当解释器遇到import语句时,如果模块在当前搜索路径就会被导入。搜索路径是一个解释器,会先搜索所有目录的列表。
当我们使用import语句时,Python解释器怎样找到对应的文件呢?这涉及Python的搜索路径,搜索路径由一系列目录名组成,Python解释器会依次从这些目录中寻找引入的模块。看起来很像环境变量,事实上可以通过定义环境变量的方式确定搜索路径。搜索路径是在Python编译或安装时确定的,被存储在sys模块的path变量中。查看搜索路径的方式如下:

import sys

print('Python 的搜索路径为:%s' % sys.path)

保存文件名为import_sys.py,在cmd命令窗口执行结果如下:

     D:\python\workspace>python import_sys.py
     Python的搜索路径为:['D:\\python\\workspace', 'E:\\python\\pythoninstall\\python
     35.zip', 'E:\\python\\pythoninstall\\DLLs', 'E:\\python\\pythoninstall\\lib', 'E:\\python\\pythoninstall',
'E:\\python\\pythoninstall\\lib\\site-packages']

由以上输出结果看到,sys.path输出了一个列表,第一项输出的是执行文件所在的目录,即我们执行Python解释器的目录(如果是脚本,就是运行脚本所在的目录)。
了解搜索路径的概念后,可以在脚本中修改sys.path引入一些不在搜索路径中的模块。
上面我们初步引入了import语句,除了用import引入模块外,还有另一种方式引入模块,先看交互模式下输入的示例:

>>> from math import pi
>>> print (pi)
3.141592653589793

上面的操作使用了from math import pi的方式,这是什么意思呢?
在Python中,from语句可以从模块中导入指定部分到当前命名空间中,语法如下:

from modname import name1[, name2[, ... nameN]]

例如,from math import pi语句就是从math模块中导入pi到当前命名空间,该语句不会将math整个模块导入。比如在math模块中还有sin、exp函数,在这个语句里这两个函数都使用不了,而在导入整个math模块的语句中可以使用。在交互模式下输入:

>>> import math
>>> print(math.pi)        #math.pi 可以被输出
3.141592653589793
>>> print(math.sin(1))    #math.sin(1)可以被输出
0.8414709848078965
>>> print(math.exp(1))    #math.exp(1)可以被输出
2.718281828459045
>>> from math import pi
>>> print (pi)            #pi 可以被输出
3.141592653589793
>>> print(sin(1))         #sin(1)不可以被输出
Traceback (most recent call last):
  File "<pyshell#51>", line 1, in <module>
    print(sin(1))
NameError: name 'sin' is not defined
>>> print(exp(1))         #exp(1)不可以被输出
Traceback (most recent call last):
  File "<pyshell#52>", line 1, in <module>
    print(exp(1))
NameError: name 'exp' is not defined

由以上输出结果可知,如果导入模块,就会得到模块中所有对象;如果指定导入某个对象,就只能得到该对象。
这样做的好处是什么呢?先看如下示例:

>>> import math
>>> print(math.pi)
3.141592653589793
>>> print(pi)
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    print(pi)
NameError: name 'pi' is not defined
>>> from math import pi
>>> print(pi)
3.141592653589793

由上面的输出结果可知,如果在导入math模块时访问pi对象,需要使用math.pi,直接使用pi访问不了,会报错。使用import语句后,可以直接访问pi对象,不需要加上模块名进行访问。
如果要访问模块中多个对象,是否需要一个一个导入呢?如果要访问math中的pi和sin,是否要写两个from math import语句?例如:

from math import pi
from math import sin

当然不用,可以直接使用如下语句:

from math import pi,sin

可以从一个导入语句导入多个函数,多个函数之间用逗号分割。
如果要访问模块中多个对象,是否需要一个一个导入呢?当然不用,可以直接使用如下语句:

from math import *

使用该语句可以将math中所有对象都引入,比如上面几个报错的情况就可以成功输出结果了,例如:

>>> from math import *
>>> print(pi)           #pi可以被输出
3.141592653589793
>>> print(sin(1))       #sin(1)可以被输出
0.8414709848078965
>>> print(exp(1))       #exp(1)可以被输出
2.718281828459045

由输出结果看到,pi、sin、exp等函数都可以被正确输出了。这是一个简单地将项目中所有模块都导入的方法。在实际开发中,这种声明不建议过多使用,这样不利于编写清晰、简单的代码。只有想从给定模块导入所有功能时才使用这种方式。
除了上述几种方式外,还可以为模块取别名,例如:

>>> import math as m
>>> m.pi
3.141592653589793

由输出结果看到,给模块取别名的方式为:在导出模块的语句末尾增加一个as子句,as后面跟上别名名称。
既然可以为模块取别名,当然也可以为函数取别名,例如:

>>> from math import pi as p
>>> p
3.141592653589793

由输出结果可知,我们为pi取了别名为p,为函数取别名的方式和为模块取别名的方式类似,也是在语句后面加上as,as后跟上别名名称。
2 使用逗号输出
我们在前面的章节已经看到许多使用逗号输出的示例,例如:

>>> student='小智'
>>> print('学生称呼:',student)
学生称呼: 小智

这种方式还可以输出多个表达式,只要将多个表达式用逗号隔开就行,例如:

>>> greeting='大家好!'
>>> intriduce='我叫小智,'
>>> comefrom='我来自智慧城市。'
>>> print(greeting,intriduce,comefrom)
大家好! 我叫小智, 我来自智慧城市。

由输出结果看到,不使用格式化的方式也可以同时输出文本和变量值。

别样的赋值

之前我们介绍了很多赋值语句,在实际使用中,赋值语句还有很多特殊用法,掌握这些用法对于提高编程水平很有帮助。
1 序列解包
前面已经有不少赋值语句的示例,比如变量和数据结构成员的赋值,不过赋值的方法不止这些,例如:

>>> x,y,z=1,2,3
>>> print(x,y,z)
1 2 3

由输出结果看到,可以多个赋值操作同时进行。后面再遇到对多个变量赋值时,就不需要对一个变量赋完值再对另一个变量赋值了,用一条语句就可以搞定,例如:

>>> x,y,z=1,2,3
>>> x,y=y,x
>>> print(x,y,z)
2 1 3

由输出结果看到,x和y的值交换了,所以可以交换两个或多个变量的值。
在Python中,交换所做的事情叫作序列解包(sequence unpacking)可选迭代解包,即将多个值的序列解开,然后放到变量序列中。可以通过下面的示例理解:

>>> nums=1,2,3
>>> nums
(1, 2, 3)
>>> x,y,z=nums
>>> x             #获得序列解开的值
1
>>> print(x,y,z)
1 2 3

由输出结果看到,序列解包后,变量获得了对应的值。
再看另一个示例:

>>> student={'name':'小萌','number':'1001'}
>>> key,value=student.popitem()
>>> key
'number'
>>> value
'1001'

由输出结果可知,此处作用于元组,使用popitem方法将键-值作为元组返回,返回的元组可以直接赋值到两个变量中。
序列解包允许函数返回一个以上的值并打包成元组,然后通过一个赋值语句进行访问。这里有一点要注意,解包序列中的元素数量必须和放置在赋值符号“=”左边的数量完全一致,否则Python会在赋值时引发异常,例如:

>>> x,y,z=1,2,3
>>> x,y,z
(1, 2, 3)
>>> x,y,z=1,2
Traceback (most recent call last):
  File "<pyshell#45>", line 1, in <module>
    x,y,z=1,2
ValueError: not enough values to unpack (expected 3, got 2)
>>> x,y,z=1,2,3,4,5
Traceback (most recent call last):
  File "<pyshell#46>", line 1, in <module>
    x,y,z=1,2,3,4,5
ValueError: too many values to unpack (expected 3)

由以上输出结果看到,当右边的元素数量和左边的变量数量不一致时,执行结果就会报错。错误原因是没有足够的值解包(左边变量多于右边元素)或多个值未解包(左边变量少于右边元素)。
谨记在操作序列解包时,要注意保证左边和右边数量的相等。
2 链式赋值
序列解包在对不同变量赋不同的值时非常有用,赋相同的值时用序列解包也可以实现。其实还可以使用其他方法,如链式赋值(Chained Assignment,例如:

>>> x=y=z=10
>>> x
10

由输出结果可知,可以通过多个等式为多个变量赋同一个值,这种方法叫作链式赋值。
链式赋值是将同一个值赋给多个变量。
上面的语句效果和下面的语句效果一样:

>>> x=10
>>> y=x
>>> y
10

由输出结果可知,既可以使用链式方式赋值,也可以单独赋值,显然链式方法更简洁。
3 增量赋值
使用赋值运算符时没有将表达式写成类似x=x+1的形式,而是将表达式放置在赋值运算符(=)的左边(如将x=x+1写成x+=1),这种写法在Python中叫作增量赋值(Augemented Assignment)。这种写法对*(乘)、/(除)、%(取模)等标准运算符都适用,例如:

>>> x=5
>>> x+=1  #加
>>> x
6
>>> x-=2 #减
>>> x
4
>>> x*=2 #乘
>>> x
8
>>> x/=4  #除
>>> x
2.0

由操作结果可以看到,使用增量赋值相对赋值操作看上去更简洁。
增量赋值除了适用于数值类型,还适用于二元运算符的数据类型,例如:

>>> field ='Hello,'
>>> field += 'world'
>>> field
'Hello,world'
>>> field*=2
>>> field
'Hello,worldHello,world'

由操作结果可知,增量赋值操作也可以用于字符串。
增量赋值可以让代码在很多情况下更易读,也可以帮助我们写出更紧凑、简练的代码。

语句块

语句块并非一种语句,语句块是一组满足一定条件时执行一次或多次的语句。语句块的创建方式是在代码前放置空格缩进。
同一段语句块中每行语句都要保持同样的缩进,如果缩进不同,Python编译器就会认为不属于同一个语句块或认为是错误的。
在Python中,冒号(:)用来标识语句块的开始,语句块中每一个语句都需要缩进(缩进量相同)。当退回到和已经闭合的块一样的缩进量时,表示当前语句块已经结束了。

条件语句

到目前为止,我们编写的程序都是简单地按语句顺序一条一条执行的。那么将介绍让程序选择执行语句的方法。

1 布尔变量的作用

布尔变量我们在第2章已经有所接触,第2章的运算符中多处提到的True、False就是布尔变量,布尔变量一般对应的是布尔值(也称作真值,布尔值这个名字是根据对真值做过大量研究的George Boole命名的)。
下面的值在作为布尔表达式时,会被解释器看作假(false):

False None 0 "" () [] {}

换句话说,标准值False和None、所有类型的数字0(包括浮点型、长整型和其他类型)、空序列(如空字符串、空元组和空列表)以及空字典都为假。其他值都为真,包括原生的布尔值True。
Python中所有值都能被解释为真值,这可能会让你不太明白,但理解这点非常有用。在Python中,标准的真值有TrueFalse两个。在其他语言中,标准的真值为0(表示假)和1(表示真)。事实上,True和False只不过是1和0的另一种表现形式,作用相同,例如:

>>> True
True
>>> False
False
>>> True == 1
True
>>> False == 0
True
>>> True+False+2
3

由上面的输出结果看到,在Python中,True和1等价,False和0等价。
布尔值True和False属于布尔类型,bool函数可以用来转换其他值,例如:

>>> bool('good good study')
True
>>> bool('')
False
>>> bool(3)
True
>>> bool(0)
False
>>> bool([1])
True
>>> bool([])
False
>>> bool()
False

由输出结果看到,可以用bool函数做boolean值转换。
因为所有值都可以用作布尔值(真值),所以几乎不需要对它们进行显式转换,Python会自动转换这些值。
注意:尽管[]和""都为假,即bool([])==bool("")==False,不过它们本身不相等,即[]!=""。其他不同类型的假值也是如此,如()!=False。

2 if语句

真值可以联合使用,首先看如下代码:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
#if 基本用法

greeting='hello'
if greeting == 'hello':
    print('hello')

该示例执行结果如下:

hello

该示例为if条件执行语句的一个实现示例。如果条件(在if和冒号之间的表达式)判定为真,后面的语句块(本例中是print语句)就会被执行;如果条件判定为假,语句块就不会被执行。

上述示例代码的执行过程如图所示。


if条件语句执行过程

图中的小黑点为if语句的起点,往后执行到条件语句(条件语句如greeting=='hello'),如果条件为真,就执行条件代码,然后结束这个if条件语句;如果条件为假,就跳过这段条件代码,这个if条件语句直接结束。
在if语句块中还可以执行一些复杂操作,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

#if 基本用法
greeting='hello'
if greeting == 'hello':
    student={'小萌': '1001', '小智': '1002', '小强': '1005','小张': '1006'}
    print('字典元素个数为:%d 个' % len(student))
    student.clear()
    print('字典删除后元素个数为:%d 个' % len(student))

以上程序执行结果为:

字典元素个数为:4 个
字典删除后元素个数为:0 个

此处的if语句块由多条语句组成,编写过程中要注意保持语句的缩进一致,否则在执行时会报错。
if语句的条件判定除了使用==外,还可以使用>(大于)、<(小于)、>=(大于等于)、<=(小于等于)等条件符表示大小关系。除此之外,还可以使用各个函数或方法返回值作为条件判定。使用条件符的操作和使用==一样,使用函数或表达式的操作在会逐步介绍。
提示:在练习过程中,要习惯编写示例中开头两行的写法,即习惯写以下两行代码:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

习惯写这两行代码可以帮助你在代码移植和编码问题上避免很多问题。代码移植时,如果从Windows移植到Linux系统上,就必须加上第一行代码。代码中有中文时,如果不加第二行,就很容易出现乱码的情况。

3 else子句

在if语句的示例中,当greeting的值为hello时,if后面的条件执行结果为true,进入下面的语句块中执行相关语句。如果greeting的值不是hello,就不能进入语句块,如果想显示相关提示,比如告诉我们greeting的值不为hello或执行的不是if中的语句块,该怎么办呢?例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-
greeting='hi'
if greeting == 'hello':
    print('hello')
else:
    print('该语句块不在if 中,greeting 的值不是hello')

这段程序加入了一个新条件子句——else子句。之所以叫子句,是因为else不是独立语句,只能作为if语句的一部分。使用else子句可以增加一种选择。
该程序的输出结果如下:
该语句块不在if 中,greeting 的值不是hello
由输出结果看到,if语句块没有被执行,执行的是else子句中的语句块。同if语句一样,else子句中的语句块也可以编写复杂语句。
注意:在else子句后面没有条件判定。

4 elif子句

在else子句的示例中,如果除if条件外,还有多个子条件需要判定,该怎么办呢?
Python为我们提供了一个elif语句,elif是else if的简写,意思为具有条件的else子句,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 10
if num > 10:
    print('num 的值大于10')
elif 0<=num<=10:
    print('num 的值介于0 到10 之间')
else:
    print('num 的值小于0')

由以上程序可知,elif需要和if、else子句联合使用,不能独立使用,并且必须以if语句开头,可以选择是否以else子句结尾。
程序输出结果如下:

num的值介于0 到10 之间

由输出结果得知,这段程序执行的是elif子句中的语句块,即elif子句的条件判定结果为true,所以执行这个子句后的语句块。

5 嵌套代码块

我们前面讲述了if语句、else子句、elif子句,这几个语句可以进行条件的选择判定,不过我们在实际项目开发中经常需要一些更复杂的操作,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 10
if num%2==0:
    if num%3==0:
         print ("你输入的数字可以整除 2 和 3")
    elif num%4==0:
         print ("你输入的数字可以整除 2 和 4")
    else:
         print ("你输入的数字可以整除 2,但不能整除 3 和 4")
else:
    if num%3==0:
         print ("你输入的数字可以整除 3,但不能整除 2")
    else:
         print ("你输入的数字不能整除 2 和 3")

由上面的程序可知,在if语句的语句块中还存在if语句、语句块以及else子句,else子句的语句块中也存在if语句和else子句。
上面的程序输出结果如下:

你输入的数字可以整除 2,但不能整除 3 和 4

由输出结果可以看出,执行的是if语句块中else子句的语句块。
在Python中,该示例使用的这种结构的代码称作嵌套代码。所谓嵌套代码,是指把if、else、elif等条件语句再放入if、else、elif条件语句块中,作为深层次的条件判定语句。

6 更多操作

我们简单介绍过一些运算符,现在将对其中一些涉及条件运算的运算符做进一步讲解。

1. is:同一性运算符

is运算符比较有趣。我们先看如下程序:

>>> x=y=[1,2,3]
>>> z=[1,2,3]
>>> x==y
True
>>> x==z
True
>>> x is y
True
>>> x is z
False

在最后一个输出语句之前,一切看起来非常美好,都在意料中,不过最后一个语句却出现了问题,为什么x和z相等却不相同呢?
这是因为is运算符用于判定同一性而不是相等性。变量x和y被绑定在同一个列表上,而变量z被绑定在另一个具有相同数值和顺序的列表上。它们的值可能相等,却不是同一个对象。
也可以从内存的角度思考,即它们所指向的内存空间不一样,x和y指向同一块内存空间,z指向另一块内存空间。
是不是看起来有些不可理喻,再看如下示例:

>>> x=[1,2,3]
>>> y=[1,5]
>>> x is not y
True
>>> del x[2]
>>> x
[1, 2]
>>> y[1]=2
>>> y
[1, 2]
>>> x==y
True
>>> x is y
False

在上面的程序中,列表x和y一开始是不同的列表,后面将列表值更改为相等,但还是两个不同的列表,即两个列表值相等却不等同。
综上所述,使用==运算符判定两个对象是否相等,使用is判定两个对象是否等同(是否为同一对象)。
提示:尽量避免用is运算符比较数值和字符串这类不可变值。由于Python内部操作这些对象方式的原因,使用is运算符的结果是不可预测的,除非你对堆栈有一定熟悉程度,否则很难预测运算结果。

2. 比较字符串和序列

字符串可以按照字母排列顺序进行比较,我们在前面的章节已经介绍过。这里介绍其他序列的比较操作。
其他序列比较的不是字符而是元素的其他类型,例如:

>>> [1,2]<[2,1]
True
>>> [1,2]<[1,2]
False
>>> [1,2]==[1,2]
True

由操作结果可知,也可以对列表进行比较操作。
如果一个序列中包括其他序列元素,比较规则也适用于序列元素,例如:

>>> [2,[1,2]]<[2,[1,3]]
True

由操作结果看到,也可以对嵌套列表进行比较操作。

3. 布尔运算符

前面我们已经讲述过不少布尔运算的操作。不过有时要检查一个以上的条件,如果按照前面的操作方式,就会多走一些弯路,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

num = 10
if num <= 10:
    if num>=5:
         print('num的值介于5 到10 之间')
    else:
         print('num的值不介于5 到10 之间')
else:
    print('num 的值不介于5 到10 之间')

上面的程序在写法上没什么问题,但是走了一些不必要的弯路,可以将代码编写得更简洁:

if num <= 10 and num>=5:
    print('num 的值介于5 到10 之间')
else:
    print('num 的值不介于5 到10 之间')

或者:

if 5 <= num <= 10:
    print('num 的值介于5 到10 之间')
else:
    print('num 的值不介于5 到10 之间')

上面的程序明显更加简洁、易读。
and运算符用于连接两个布尔值,并在两者都为真时返回真,否则返回假。与and同类的还有or和not两个运算符。
布尔运算符有一个有趣的特性:只有在需要求值时才求值。举例来说,表达式x and y需要两个变量都为真时才为真,所以如果x为假,表达式就立刻返回false,无论y的值是多少。实际上,如果x为假,表达式就会返回x的值,否则返回y的值。这种行为被称为短路逻辑(short-circuit logic)惰性求值(lazy evaluation)。布尔运算符通常被称为逻辑运算符,这种行为同样适用于or。在表达式x or y中,x为真时直接返回x的值,否则返回y值。注意,这意味着在布尔运算符后面的代码都不会被执行。

7 断言

在Python中,有一个和if语句工作方式非常相近的关键字,其工作方式类似如下伪代码:

if not condition:
    crash program

在Python中为什么需要这样的代码呢?
在没完善一个程序之前,我们不知道程序会在哪里出错,与其在运行时崩溃,不如在出现错误条件时就崩溃。一般来说,可以要求一些条件必须为真。在Python中,assert关键字能实现这种工作方式。先来看一个示例:

>>> x=3
>>> assert x > 0, "x is not zero or negative"
>>> assert x%2 == 0, "x is not an even number" #提示x 不是偶数
Traceback (most recent call last):
  File "<pyshell#81>", line 1, in <module>
    assert x%2 == 0, "x is not an even number"
AssertionError: x is not an even number

由上面的输出结果看到,当assert后面的条件为真时,程序正常运行;当assert后面的条件为假时,输出错误信息。错误的提示信息由我们自己定义,这个错误提示信息可以称为异常参数。assert的异常参数是在断言表达式后添加的字符串信息,用来解释断言并更容易知道问题出在哪里。
使用assert断言是学习Python的好习惯,Python assert断言句语格式及用法很简单。
使用assert断言时,要注意以下几点:
(1)assert断言用来声明某个条件是真的。
(2)如果你非常确信你使用的列表中至少有一个元素,想要检验这一点,并在它非真时引发一个错误,那么assert语句是应用在这种情形下的理想语句。
(3)assert语句失败时,会引发一个AssertionError。

循环

程序在一般情况下是按顺序执行的。编程语言提供了各种控制结构,允许更复杂的执行路径。循环语句允许我们多次执行一个语句或语句组。图6-6所示为大多数编程语言中循环语句的执行流程。


循环语句执行流程

我们已经知道条件为真(或假)时程序如何执行了。若想让程序重复执行,该怎么办呢?比如输出1~100所有数字,是写100个输出语句吗?显然你不想这样做。接下来我们学习如何解决这个问题。

1 while循环

我们先看如何使用简单的程序输出1~100所有数字,程序如下:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

n=1
while n<=100:
    print('当前数字是:',n)
    n += 1

由输入程序看到,只需短短几行就可以实现这个功能,我们看输出结果(由于全部输出会太长,也没有必要,此处显示几行输出结果作为展示):

当前数字是: 1
当前数字是: 2
当前数字是: 3
当前数字是: 4
当前数字是: 5
……

由输出结果看到,按顺序输出了对应结果。

该示例中使用了while关键字。在Python编程中,while语句用于循环执行程序,以处理需要重复处理的任务。基本语法形式为:

while 判断条件:
 执行语句……

执行语句可以是单个语句或语句块。判断条件可以是任何表达式,所有非零、非空(null)的值都为真(true)。当判断条件为假(false)时,循环结束。


while循环执行流程

该流程图的意思为:首先对while条件判定,当条件为true时,执行条件语句块,执行完语句块再判定while条件,若仍然为true,则继续执行语句块,直到条件为false时结束循环。

2 for循环

上面讲述了while循环,可以看到while语句非常灵活。例如以下代码:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

n=0
fields=['a','b','c']
while n<len(fields):
    print('当前字母是:',fields[n])
    n += 1

该代码实现的功能是将列表中的元素分别输出。该程序的实现没有什么问题,我们是否有更好的方式实现这个功能呢?答案是有,例如:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

fields=['a','b','c']
for f in fields:
    print('当前字母是:',f)

可以看到,代码比前面使用while循环时更简洁,代码量也更少。程序执行输出结果如下:

当前字母是: a
当前字母是: b
当前字母是: c

该示例使用了for关键字。在Python中,for关键字叫做for循环,for循环可以遍历任何序列的项目,如一个列表或字符串。for循环的语法格式如下:

for iterating_var in sequence:
statements(s)

sequence是任意序列,iterating_var是序列中需要遍历的元素。statements是待执行的语句块。
for循环的执行流程如图所示。

for循环执行流程

该流程图的意思为:首先对for条件判定,游标(后面会详细讲解这个词)指向第0个位置,即指向第一个元素,看sequence序列中是否有元素,若有,则将元素值赋给iterating_var,接着执行语句块,若语句块中需要获取元素值,则使用iterating_var的值,执行完语句块后,将序列的游标往后挪一个位置,再判定该位置是否有元素,若仍然有元素,则继续执行语句块,然后序列的游标再往后挪一个位置,直到下一个位置没有元素时结束循环。
我们再看以下示例:

#! /usr/bin/python3
# -*- coding:UTF-8 -*-

print('-----for 循环字符串-----------')
for letter in 'good': #for 循环字符串
   print ('当前字母 :', letter)

print('-----for 循环数字序列-----------')
number=[1,2,3]
for num in number:     #for 循环数字序列
    print('当前数字:',num)

print('-----for 循环字典-----------')
tups={'name':'小智','number':'1002'}
for tup in tups: #for 循环字典
    print('%s:%s' % (tup,tups[tup]))
输出结果如下:
-----for 循环字符串-----------
当前字母 : g
当前字母 : o
当前字母 : o
当前字母 : d
-----for 循环数字序列-----------
当前数字: 1
当前数字: 2
当前数字: 3
-----for 循环字典-----------
number:1002
name:小智

由上面的输入代码和输出结果可以看到,for循环的使用还是比较方便的。
提示:如果能使用for循环,就尽量不要使用while循环。

3 循环遍历字典元素

在上面的示例中我们已经提供了使用for循环遍历字典的代码,代码如下:

tups={'name':'小智','number':'1002'}
for tup in tups: #for 循环字典
    print('%s:%s' % (tup,tups[tup]))

可以看到,此处用for循环对字典的处理看起来有一些繁杂,是否可以使用更直观的方式处理字典呢?
还记得我们前面学习的序列解包吗?for循环的一大好处是可以在循环中使用序列解包,例如:

tups={'name':'小智','number':'1002'}
for key,value in tups.items():
    print('%s:%s' % (key,value))
输出结果如下:
number:1002
name:小智

由输入代码和输出结果看到,可以使用items方法将键-值对作为元组返回。
注意:字典中的元素是没有顺序的。也就是说,迭代时字典中的键和值都能保证被处理,但是处理顺序不确定。这也是用for循环输出字典中的元素时不按照顺序输出的原因。

4 迭代工具

在Python中,迭代序列或其他可迭代对象时,有一些函数非常有用。下面我们介绍一些有用的函数。

1. 并行迭代

程序可以同时迭代两个序列,输入如下:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

student=['xiaomeng','xiaozhi','xiaoqiang']
number=[1001,1002,1003]
for i in range(len(student)):
    print(student[i],'的学号是:',number[i])

程序执行结果如下:

xiaomeng 的学号是:1001
xiaozhi 的学号是:1002
xiaoqiang 的学号是:1003

在程序中,i是循环索引的标准变量名。
在Python中,内建的zip函数用来进行并行迭代,可以把两个序列合并在一起,返回一个元组的列表,例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

student=['xiaomeng','xiaozhi','xiaoqiang']
number=[1001,1002,1003]
for name,num in zip(student,number):
    print(name,'的学号是:',num)

程序执行结果和前面一样。
zip函数可以作用于任意数量的序列,并且可以应付不等长的序列,当短序列“用完”时就会停止。

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

for num1,num2 in zip(range(3),range(100)):
    print('zip 键值对为:',num1,num2)

程序执行结果如下:

zip 键值对为: 0 0
zip 键值对为: 1 1
zip 键值对为: 2 2

由输出结果看到,zip函数以短序列为准,当短序列遍历结束时,for循环就会遍历结束。
提示:此处用到range函数,是Python 3中的函数,在Python 2版本中存在与这个函数功能类似的xrange函数。

2. 翻转和排序迭代

我们在列表中学习过reverse和sort方法,此处介绍两个类似的函数——reversedsorted函数。这两个函数可作用于任何序列或可迭代对象,但不是原地修改对象,而是返回翻转或排序后的版本。在交互模式下输入:

>>> sorted([5,3,7,1])
[1, 3, 5, 7]
>>> sorted('hello,world!')
['!', ',', 'd', 'e', 'h', 'l', 'l', 'l', 'o', 'o', 'r', 'w']
>>> list(reversed('hello,world!'))
['!', 'd', 'l', 'r', 'o', 'w', ',', 'o', 'l', 'l', 'e', 'h']
>>> ''.join(reversed('hello,world!'))
'!dlrow,olleh'

由输出结果我们看到,sorted函数返回的是一个列表,reversed函数返回的是一个可迭代对象。它们的具体含义不用过多关注,在for循环和join方法中使用不会有任何问题。如果要对这两个函数使用索引、分片及调用list方法,就可以使用list类型转换返回对象。

5 跳出循环

我们在前面的示例中讲过,循环会一直执行,直到条件为假或序列元素用完时才会结束。若我们想提前中断循环,比如循环的结果已经是我们想要的了,不想让循环继续执行而占用资源,有什么方法可以实现呢?
Python提供了breakcontinue等语句可用于这种情形。

1. break

break语句用来终止循环语句,即使循环条件中没有False条件或序列还没有遍历完,也会停止执行循环语句。
break语句用在while和for循环中。
如果使用嵌套循环,break语句就会停止执行最深层的循环,并开始执行下一行代码。
break语句语法如下:

break

break语句的执行流程如图所示。


break执行流程

当遇到break语句时,无论执行什么条件,都跳出这个循环,例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

for letter in 'hello':  #示例1
   if letter == 'l':
      break
   print ('当前字母为:', letter)

num = 10                      #示例2
while num > 0:
   print ('输出数字为:', num)
   num -= -1
   if num == 8:
      break

输出结果如下:

当前字母为: h
当前字母为: e
输出数字为: 10
输出数字为: 9

[图片上传失败...(image-c5ab9b-1567490535468)]

图6-10 continue执行流程

由输出结果看到,在示例1中,输出语句输出循环遍历到的字符,当遇到指定字符时,跳出for循环。在示例2中,使用while做条件判定,在语句块中输出满足条件的数字,当数字等于8时,跳出while循环,不再继续遍历。

2. continue

continue语句用来告诉Python跳过当前循环的剩余语句,然后继续进行下一轮循环。

continue语句用在while和for循环中。

continue语句的语法格式如下:

continue

continue语句的执行流程如图所示。


continue执行流程

当执行过程中遇到continue语句时,无论执行条件是真还是假,都跳过这次循环,进入下一次循环,例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

for letter in 'hello': # 示例1
   if letter == 'l':
      continue
   print ('当前字母 :', letter)

num = 3                       # 示例2
while num > 0:
num -= 1
if num == 2:
   continue
print ('当前变量值 :', num)

输出结果如下:

当前字母 : h
当前字母 : e
当前字母 : o
当前变量值 : 1
当前变量值 : 0

由输出结果看到,相比于break语句,使用continue语句只是跳过一次循环,不会跳出整个循环。

6 循环中的else子句

在开发过程中,可能需要在while、for等循环不满足条件时做一些工作。该怎么实现呢?下面进行介绍。

1. 在while循环中使用else 语句

在while条件语句为false时,执行else的语句块,例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

num = 0
while num < 3:
   print (num, " 小于 3")
   num = num + 1
else:
   print (num, " 大于或等于 3")
print("结束循环!")

执行结果如下:

0 小于 3
1 小于 3
2 小于 3
3 大于或等于 3
结束循环!

由输出结果看到,while循环结束后执行了else语句中的语句块,输出“3大于或等于3”语句。

2. 在for 循环中使用else 语句

在for条件语句为false或结束后没有被break中断时,执行else的语句块,例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

names = ['xiaomeng', 'xiaozhi']
for name in names:
    if name == "xiao":
         print("名称:",name)
         break
    print("循环名称列表 " + name)
else:
    print("没有循环数据!")
print("结束循环!")

程序执行结果如下:

循环名称列表 xiaomeng
循环名称列表 xiaozhi
没有循环数据!
结束循环!

由输出条件看到,for循环结束后执行了else语句块中的内容。

pass语句

Python中的pass是空语句,作用是保持程序结构的完整性。pass语句的语法格式如下:

pass

pass不做任何事情,只是占位语句,例如:

>>> pass
>>>

输出结果什么都没有做。
为什么使用一个什么都不做的语句呢?再来看如下代码:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

name='xiaomeng'
if name == 'xiaomeng':
    print('hello')
elif name == 'xiaozhi':
    #预留,先不做任何处理
else:
    print('nothing')

执行程序,结果如下:

  File "itertor.py", line 63
    else:
        ^
IndentationError: expected an indented block

执行报错了,因为程序中有空代码,在Python中空代码是非法的。解决办法是在语句块中加一个pass语句。上面的代码更改为:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

name='xiaomeng'
if name == 'xiaomeng':
    print('hello')
elif name == 'xiaozhi':
    #预留,先不做任何处理
    pass
else:
    print('nothing')

再执行这段代码,得到结果如下:

hello

输出结果可以正确执行了。

8 牛刀小试——猜字游戏编写

为巩固学习内容,设计一个小游戏帮助我们系统地温习本章的知识点。
游戏内容是这样的:随便给定一个在一定范围内的数字,让用户去猜这个数字是多少,并输入自己猜测的数字,系统判断是否为给定数字。如果输入的猜测数字大于给定值,提示你输入的值大了;如果输入的值小于给定值,就提示输入的值小了;如果等于给定数字,就提示你猜对了,并展示猜了多少次猜中了。
在看参考代码之前先思考一下,要实现这个小游戏,你会怎么做呢?
思考点拨:
先从最简单的方向思考,有3种情况:
(1)输入值小于给定值。
(2)输入值等于给定值。
(3)输入值大于给定值。
对于情况(1)和(3),需要继续输入;对于情况(2),输入结束。
需要提供3个变量:一个变量用于记录给定值,一个变量用于记录输入值,一个变量用于记录输入了多少次,注意输入次数至少是一次。
参考代码如下(参考代码对输入元素是否为数字做了判断,同时判断了输入数字是否超出给定的数值范围):

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

import random

number = random.randint(1,100)
guess = 0
while True:
    num_input = input("请输入一个1 到100 的数字:")
    guess +=1
    if not num_input.isdigit():
         print ("请输入数字。")
    elif int(num_input)<0 or int(num_input)>=100:
         print ("输入的数字必须介于1 到100。")
    else:
         if number==int(num_input):
             print ("恭喜您,您猜对了,您总共猜了 %d 次"%guess)
             break
         elif number>int(num_input):
             print ("您输入的数字小了。")
         elif number<int(num_input):
             print ("您输入的数字大了。")
         else:
             print ("系统发生不可预测问题,请联系管理人员进行处理。

9 Python程序调试

这里通过设置的一些错误让读者认识在编写代码过程中的常见问题,以帮助读者熟悉和解决实际遇到的问题。
(1)在交互模式下输入false,看会输出什么结果,并尝试解答为什么输出这样的结果。输入true、true+false呢?

>>> false
Traceback (most recent call last):
  File "<pyshell#38>", line 1, in <module>
    false
NameError: name 'false' is not defined

(2)在while或for循环中,尝试不对齐循环语句块中的语句,看看执行结果是怎样的?例如:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

num = 10
while num > 0:
   print ('输出数字为:', num)
  num -= 1        #本行与其他行不对齐
   if num == 8:
      break

运行这段代码,查看输出结果是怎样的,并尝试更改为for循环,再次查看结果。
(3)尝试以下程序的执行结果:

#! /usr/bin/python3
#-*- coding:UTF-8 -*-

name='xiaomeng'
if name == 'xiaomeng':
    print('hello')
elif name == 'xiaozhi':
    print('do nothing')
        pass
else:
    print('nothing')
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容

  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,720评论 0 10
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,176评论 0 9
  • 条件与循环语句 条件与循环语句的表达式 在选择结构和循环结构中,都要根据条件表达式的值来确定下一步的执行流程,条件...
    backup备份阅读 2,321评论 0 0
  • 害怕别人不了解 人海中剩一颗心漂泊 害怕别人太了解 来往只沦为杯盏交错 害怕不喜欢一个人 值得纪念的青春无...
    沅礼阅读 231评论 0 0
  • 明日复明日, 明日何其多? 既然那么多, 不妨在拖拖。
    大栗粗奇迹阅读 274评论 0 2