1.布尔(Boolean)值和布尔变量
在讲条件语句之前,首先应该了解一下布尔类型。条件语句(if)需要为其指定布尔值或布尔类型的变量,才能根据条件判断是否要指定代码块中的语句。布尔值只有两个值:True和False,可以将这两个值翻译成“真”和“假”。
现在我们已经了解了布尔值是用来做什么的,但Python语言会将哪些值看做布尔值呢?其实在Python语言中,每一种类型的值都可以被解释成布尔类型的值。例如,下面的值都会被解释成布尔值中的False。
None 0 "" () [] {}
这些值所涉及到的数据类型有一些我们到现在为止并没有讲过(例如,[]表示长度为0的列表),不过读者也不用担心,在后面的章节会详细讲解这些数据类型。
如果在条件语句中使用上面的这些值,那么条件语句中的条件都会被解释成False,也就是说,条件代码块中的语句不会被执行。
在Python语言底层,会将布尔值True 看做1,将布尔值False看做0,尽管从表面上看,True和1、False和0是完全不同的两个值,但实际上,它们是相同的。我们可以在Python控制台验证这一点。
>>> True == 1
True
>>> False == 0
True
>>> True + False + 20
21
很明显,我们可以直接将True看成1,False看成0,也可以直接将True和False当成1和0用,所以True + False + 20的计算结果是21。
另外,我们可以用bool函数将其他类型的值转换为布尔类型的值。
>>> bool("")
False
>>> bool("Hello")
True
>>> bool([])
False
>>> bool([1,2,3])
True
>>> bool(20)
True
>>> bool('')
False
我们可以看到,在前面给出的几个会被系统认为是False的值,通过bool函数的转换,会变成真正的布尔值。不过这些值是不能直接和布尔值比较的,例如,不能直接使用“[] == false”,正确的做法是先用bool函数将其转换为布尔值,然后再比较
bool([]) == false
在前面的代码中使用了“==”运算符,这是逻辑运算符,是二元运算符,需要指定左右两个操作数用于判断两个值是否相等,如果两个操作数相等,运算结果为True,否则为False。这个运算符在后面的章节中会经常用到,当然,还有很多类似的运算符,在讲解条件语句时会一起介绍。
2. 条件语句(if、else和elif)
对于计算机程序来说,要学会的第一项技能就是“转弯”,也就是根据不同的条件,执行不同的程序分支,这样的程序才有意义。
if语句的作用就是为程序赋予这项“转弯”的技能。使用if语句就需要用到在3.3节介绍的代码块。Python语言要求当if语句的条件满足时要执行的代码块必须缩进(一般是缩进4个空格)。if语句的语法格式如下:
if logic expression: # if代码块开始
statement1
statement2
… …
statementn
otherstatement # if代码块结束
其中logic expression表示逻辑表达式,也就是返回布尔类型值(True或False)的表达式。由于Python语句的各种数据类型都可以作为用作布尔类型,所以logic expression可以看做是普通的表达式。根据代码块的规则,每一个代码块的开始行的结尾要使用冒号(:),如果if代码块结束,就会退到代码块开始行的缩进量即可。
下面是if语句的基本用法。
n = 3
if n == 3:
print("n == 3")
print("if代码块结束")
在上面这段代码中,“n == 3”是逻辑表达式,本例中的值为True。而“print("n == 3")”是if代码块中的语句,由于“n == 3”的值为True,所以“print("n == 3")”会被执行。最后一条语句不属于if代码块,所以无论if语句的条件是否为True,这行代码都会被执行。
对于条件语句来说,往往分支不只一个。例如,上面的代码如果变量n的值是4,那么if语句的条件就为False,这时要执行条件为False的分支,就可以使用else子句。
n = 4
if n == 3:
print("n == 3")
else:
print("n等于其他值")
print("if代码块结束")
在上面这段代码中,n等于4,所以if语句的条件为False,因此else代码块中的语句会被执行。if与else都是代码块,所以if语句和else语句后面都要以冒号(:)结尾。
在多分支条件语句,需要使用elif子句设置更多的条件。elif后面跟逻辑表达式,elif也是代码块,所以后面要用冒号(:)结尾。另外,在if语句中,if和else部分只能有一个,而elif部分可以有任意多个。
n = 4
if n == 3:
print("n == 3")
elif n == 4:
print("n == 4")
elif n == 5:
print("n == 5")
else:
print("n等于其他值")
print("if代码块结束")
本例通过raw_input函数从控制台输入一个名字,然后通过条件语句判断名字以什么字母开头。
from click._compat import raw_input
name = raw_input("请输入您的名字:") # 从控制台输入名字
if name.startswith("B"): # if代码块
print("名字以B开头")
elif name.startswith("F"): # elif代码块
print("名字以F开头")
elif name.startswith("T"): # elif代码块
print("名字以T开头")
else: # else代码块
print("名字以其他字母开头")
程序运行结果如下图所示。
3.嵌套代码块
条件语句可以进行嵌套,也就是说,在一个条件代码块中,可以有另外一个条件代码块。包含嵌套代码块B的代码块A可以称为B的父代码块。嵌套代码块仍然需要在父代码块的基础上增加缩进量来放置自己的代码块。下面的例子会演示如何使用嵌套代码块进行逻辑判断。
本例要求在Python控制台输入一个姓名,然后通过嵌套代码块中判断输入的姓名,根据判断结果输出结果。
name = input("你叫什么名字?") # 从Python控制台输入一个字符串(姓名)
if name.startswith("Bill"): # 以Bill开头的姓名
if name.endswith("Gates"): # 以Gates结尾的姓名(嵌套代码块)
print("欢迎Bill Gates先生")
elif name.endswith("Clinton"): # 以Clinton结尾的姓名
print("欢迎克林顿先生")
else: # 其他姓名
print("未知姓名")
elif name.startswith("李"): # 以“李”开头的姓名
if name.endswith("宁"): # 以“宁”结尾的姓名
print("欢迎李宁老师")
else: # 其他姓名
print("未知姓名")
else: # 其他姓名
print("未知姓名")
程序运行结果如下图所示。
4. 比较运算符
尽管if语句本身的知识到现在为止已经全部讲完了,不过我们的学习远没有介绍。前面给出的if语句的条件都非常简单,但在实际应用中,if语句的条件可能非常复杂,这就需要使用到本节要介绍的比较运算符。
现在先来看看下表列出的Python语言中的比较运算符。
上标描述的比较运算符中,涉及到了对象和容器的概念,目前我们还没讲这些技术,在本节读者只需了解Python语言可以通过比较运算符操作对象和容器即可,在后面介绍对象和容器的章节,会详细介绍如何利用相关比较运算符操作对象和容器。
在比较运算符中,最常用的就是判断两个值是否相等,例如,a大于b,a等于b。这些运算符包括“==”、“<”、“>”、“>=”、“<=”和“x != y”。
如果比较两个值是否相等,需要使用“==”运算符,也就是两个等号。
>>> "hello" == "hello"
True
>>> "Hello" == "hello"
False
>>> 30 == 10
False
要注意,如果比较两个字符串是否相等,会比较两个字符串中对应的每一个字母,所以“Hello”和“hello”并不相等,也就是说比较运算符是对大小写敏感的。
在使用“==”运算符时一定要注意,不要写成一个等号(=),否则就成赋值运算符了,对于赋值运算符来说,等号(=)左侧必须是一个变量,否则会抛出异常。
>>> "hello" = "hello" # 使用赋值运算符,会抛出异常
File "<stdin>", line 1
SyntaxError: can't assign to literal
>>> s = "hello"
>>> s
'hello'
对于字符串、数值等类型的值,也可以使用大于(>)、小于(<)等运算符比较他们的大小。
>>> "hello" > "Hello"
True
>>> 20 > 30
False
>>> s = 40
>>> s <= 30
False
>>> "hello" != "Hello"
True
Python语言在比较字符串时,会按字母ASCII顺序进行比较,例如,比较“hello”和“Hello”的大小。首先会比较'h'和'H'的大小,很明显'h'的ASCII大于'H'的ASCII,所以后面的都不需要比较了,因此,"hello" > "Hello"的结果是True。
如果一个字符串是另一个字符串的前缀,那么比较这两个字符串,Python语言会认为长的字符串更大一些。
>>> "hello" < "hello world"
True
除了比较大小的几个运算符外,还有用来确定两个对象是否相等的运算符,以及判断某个值是否属于一个容器的运算符,尽管现在还没有讲到对象和容器,但这里不妨做一个实验,来看看这些运算符如何使用,以便以后学习对象和容器是,更容易掌握这些运算符。
用于判断两个对象是否相等的运算符是is和is not,这两个运算符看起来和等于运算符(==)差不多,不过用起来却大有玄机。
>>> x = y = [1,2,3]
>>> z = [1,2,3]
>>> x == y
True
>>> x == z
True
>>> x is y
True
>>> x is z
False
>>> x is not z
True
在上面的代码中,使用“==”和“is”比较x和y时结果完全一样,不过在比较x和z时,就会体现出差异。x == z的结果是True,而x is z的结果却是False。出现这样的结果,原因是“==”运算符比较的是对象的值,x和z的值都是一个列表(也可以将列表看做是一个对象),并且列表中的元素个数和值完全一样,所以x == z的结果是True。但“is”运算符用于判断对象的同一性,也就是说,不仅对象的值要完全一样,而且对象本身还要是同一个对象,很明显,x和y是同一个对象,因为在赋值时,先将一个列表赋给y,然后再将y的值赋给x,所以x和y指向了同一个对象,而z另外赋值了一个列表,所以z和x、y尽管值相同,但并不是指向的同一个对象,因此,x is z的结果就是False。
判断某个值是否属于一个容器,要使用“in”和“not in”运算符。下面的代码首先定义一个列表变量x,然后判断变量y和一些值是否属于x。
>>> x = [1,2,3,4,5] # 定义一个列表变量
>>> y = 3
>>> 1 in x
True
>>> y in x
True
>>> 20 in x
False
>>> 20 not in x
True
“in”和“not in”运算符也可以用于判断一个字符串是否包含另外一个字符串,也就是说,可以将字符串看做字符或子字符串的容器。
>>> s = "hello world"
>>> 'e' in s
True
>>> "e" in s
True
>>> "x" in s
False
>>> "x" not in s
True
>>> "world" in s
True
如果遇到需要将多个逻辑表达式组合在一起的情况,需要用到逻辑与(and)、逻辑或(or)和逻辑非(not)。逻辑与的运算规则是只有“x and y”中的x和y都为True时,运算结果才是True,否则为False。逻辑或的运算规则是只有“x or y”中的x和y都为False时,运算结果才是False,否则都为True。逻辑非的运算规则是“not x”中,x为True,运算结果为False,x为False,运算结果为True。
>>> 20 < 30 and 40 < 50
True
>>> 20 > 40 or 20 < 10
False
>>> not 20 > 40
True
本例演示了比较运算符的基本用法。
print(20 == 30) # 判断20和30是否相等,运行结果:False
x = 20
y = 40
print(x < y) # 判断x是否小于y,运行结果:True
if x > y: # 条件不满足
print("x > y")
else: # 条件满足
print("x <= y")
s1 = "hello"
s2 = "Hello"
if s1 >= s2 and x > y: # 条件不满足
print("满足条件")
elif not s1 < s2: # 条件满足
print("基本满足条件")
else: # 条件不满足
print("不满足条件")
程序运行结果如下图所示。
5. 断言(Assertions)
断言的使用方式类似于if语句,只是在不满足条件时,会直接抛出异常。类似于下面的if语句(伪代码)
if not dondition: # 如果不满足条件,会直接抛出异常,程序会中断
crash program
那么究竟为什么需要这样的代码呢?主要是因为需要监测程序在某个地方是否满足条件,如果不满足条件,应该及时通知开发人员,而不是将这些bug隐藏起来,直到关键的时刻再崩溃。
其实在TDD(Test-driven development,测试驱动开发 )中经常使用断言,TDD会在程序发现异常时执行断言,并抛出异常。
在Python语言中,断言需要使用assert语句,在assert关键字的后面指定断言的条件表达式。如果条件表达式的值是False,那么就会抛出异常。而且断言后面的语句都不会被执行,相当于程序的一个断点。
>>> value = 20
>>> assert value < 10 or value > 30 # 条件不满足,会抛出异常
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>> assert value < 30 # 条件满足,会正常执行后面的语句
我们可以看到,value变量的值是20,而assert后面的条件是“value < 10 or value > 30”,很明显,条件不满足,所以在断言处会抛出异常。而后面的断言,条件是“value < 30”,这个条件是满足的,所以在断言后面的语句都会正常执行。
当断言条件不满足时抛出异常,在默认情况下,只显示了抛出异常的位置,并没有显示因为什么抛出异常,所以为了异常信息更明确,可以为assert语句指定异常描述。
>>> value = 20
>>> assert value < 10 or value > 30, 'value值必须在10和20之间' # 为断言指定异常描述信息
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: value值必须在10和20之间 # 显示了异常描述信息
本例演示了断言的用法。
name = "Bill" # 定义变量name
assert name == "Bill" # 断言条件表达式的值是True,继续执行下面的语句
age = 20 # 定义变量age
# 断言条件表达式的值是False,抛出异常,后面的代码不会被执行
assert 0 < age < 10, "年龄必须小于10岁"
print("hello world") # 这行代码不会被执行
程序运行结果如下图所示。