好好学Python:从零基础到项目实战
上QQ阅读APP看书,第一时间看更新

6.5 条件语句

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

6.5.1 布尔变量的作用

布尔变量我们在第2章已经有所接触,第2章的运算符中多处提到的True、False就是布尔变量,布尔变量一般对应的是布尔值(也称作真值,布尔值这个名字是根据对真值做过大量研究的George Boole命名的)。

下面的值在作为布尔表达式时,会被解释器看作假(False):

False  None 0  "" () [] {}

换句话说,标准值False和None、所有类型的数字0(包括浮点型、长整型和其他类型)、空序列(如空字符串、空元组和空列表)以及空字典都为假。其他值都为真,包括原生的布尔值True。

Python中所有值都能被解释为真值,这可能会让你不太明白,但理解这点非常有用。在Python中,标准的真值有True和False两个。在其他语言中,标准的真值为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。

6.5.2 if语句

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

# if 基本用法
if (greeting := 'hello') == 'hello':
   print('hello')

该示例执行结果如下:

hello

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

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

图6-5 if条件语句执行过程

图6-5中的小黑点为if语句的起点,往后执行到条件语句(条件语句如greeting == 'hello'),如果条件为真,就执行条件代码,然后结束这个if条件语句;如果条件为假,就跳过这段条件代码,这个if条件语句直接结束。

在if语句块中还可以执行一些复杂操作,例如(文件名为if_use.py):

# if 基本用法
if (greeting := 'hello') == 'hello':
    student={'小萌': '000', '小智': '001', '小强': '002', '小张': '003'}
    print(f'字典元素个数为:{len(student)}')
    student.clear()
    print(f'字典删除后元素个数为:{len(student)}')

以上程序执行结果为:

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

此处的if语句块由多条语句组成,编写过程中要注意保持语句的缩进一致,否则在执行时会报错。

if语句的条件判定除了使用==外,还可以使用>(大于)、<(小于)、>=(大于等于)、<=(小于等于)等条件符表示大小关系。除此之外,还可以使用各个函数或方法返回值作为条件判定。使用条件符的操作和使用==一样,使用函数或表达式的操作在后续章节会逐步介绍。

6.5.3 else子句

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

if (greeting := 'hi') == 'hello':
    print('hello')
else:
    print('该语句块不在if中,greeting的值不是hello')

这段程序加入了一个新条件子句—else子句。之所以叫子句,是因为else不是独立语句,只能作为if语句的一部分。使用else子句可以增加一种选择。

该程序的输出结果如下:

该语句块不在if中,greeting的值不是hello

由输出结果看到,if语句块没有被执行,执行的是else子句中的语句块。同if语句一样,else子句中的语句块也可以编写复杂语句。

提示

在else子句后面没有条件判定。

6.5.4 elif子句

在else子句的示例中,如果除if条件外,还有多个子条件需要判定,该怎么办呢?

Python为我们提供了一个elif语句,elif是else if的简写,意思为具有条件的else子句,例如(文件命名if_elif_use.py):

if (num := 10) > 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,所以执行这个子句后的语句块。

6.5.5 嵌套代码块

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

由上面的程序可知,在if语句的语句块中还存在if语句、语句块以及else子句,else子句的语句块中也存在if语句和else子句。

上面的程序输出结果如下:

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

由输出结果可以看出,执行的是if语句块中else子句的语句块。

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

6.5.6 更多操作

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

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.布尔运算符

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

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

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的值。注意,这意味着在布尔运算符后面的代码都不会被执行。

6.5.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 "<stdin>", line 1, in <module>
AssertionError: x is not an even number

由上面的输出结果看到,当assert后面的条件为真时,程序正常运行;当assert后面的条件为假时,输出错误信息。错误的提示信息由我们自己定义,这个错误提示信息可以称为异常参数。assert的异常参数是在断言表达式后添加的字符串信息,用来解释断言并更容易知道问题出在哪里。

使用assert断言是学习Python的好习惯,Python assert断言语句的格式及用法很简单。

使用assert断言时,要注意以下几点:

(1)assert断言用来声明某个条件是真的。

(2)如果你非常确信使用的列表中至少有一个元素,想要检验这一点,并在它非真时引发一个错误,那么assert语句是应用在这种情形下的理想语句。

(3)assert语句失败时,会引发一个AssertionError。