Python 中的运算符以及流程控制
前面已经了解了 Python 中常用的数据结构,本文接着来看 Python 中的运算符以及程序流程控制,这是一个 可执行 Python 程序实现的必要部分。
前面我们已经熟悉并深入了解了 Python 支持的六种基本数据类型,这就意味着我们已经成功突破现实世界和镜像世界数据差异的墙。
但对于 Python 世界而言,光掌握正确使用 【镜像世界的数据】 还不够,还需要正确的 【沟通逻辑】 才能让Python 准确地执行我们下达的命令,这就需要了解 Python 中的运算符以及流程控制以帮助我们完成更多的功能。
Python 中的运算符
开始后续的流程控制学习之前,我们需要系统的认识一下 Python 中的运算符。
我们先来看个简单的例子:
1 | 4 + 5 |
其中 4
,5
被称为操作数,+
被称之为运算符。
Python 语言支持以下类型的运算符:
- 算术运算符
- 赋值运算符
- 比较(关系)运算符
- 逻辑运算符
- 位运算符
- 成员运算符
- 身份运算符
本文将会针对上述运算符分类,进行分别学习:
运算符详解
算术运算符
在介绍 Number(数字)数据类型时,我们已经接触过算术运算了。这里我们来看如何使用算术运算符进行算术运算:
[1] >>>> 加法:
1 | 21 aNumber = |
当 +
用于数字(Number)时表示加法运算(加法运算符);但是当 +
用于序列时表示连接运算(连接运算符),请参照 Python 中的序列说明。
[2] >>>> 减法:
1 | >>> cNumber = aNumber - bNumber |
-
除了可以用作减法运算之外,还可以用作求负运算(正数变负数,负数变正数),即取相反数。
[3] >>>> 乘法:
1 | >>> cNumber = aNumber * bNumber |
当 *
用于数字(Number)时表示乘法运算(乘法运算符);但是当 *
用于序列时表示重复运算(重复运算符),用于将几个同样的序列连接起来,请参照 Python 中的序列说明。
[4] >>>> 除法:
1 | # 普通除法(计算结果总是小数,不管是否能除尽,也不管参与运算的是整数还是浮点数): |
注意:进行除法运算时,除数始终不能为 0
,除以 0
是没有意义的,这将导致 ZeroDivisionError
错误。在某些编程语言中,除以 0
的结果是无穷大(包括正无穷大和负无穷大)。
[5] >>>> 取模(取余):
1 | >>> aNumber = 21 |
[5] >>>> 幂运算:
1 | >>> aNumber = 21 |
赋值运算符
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c |
+= | 加法赋值运算符 | c += a 等效于 c = c + a |
-= | 减法赋值运算符 | c -= a 等效于 c = c - a |
*= | 乘法赋值运算符 | c *= a 等效于 c = c * a |
/= | 除法赋值运算符 | c /= a 等效于 c = c / a |
%= | 取模赋值运算符 | c %= a 等效于 c = c % a |
**= | 幂赋值运算符 | c **= a 等效于 c = c ** a |
//= | 取整除赋值运算符 | c //= a 等效于 c = c // a |
通常情况下,只要能使用扩展后的赋值运算符,都推荐使用这种赋值运算符。
注意:=
和 ==
是两个不同的运算符。=
用来赋值,而 ==
用来判断两边的值是否相等,千万不要混淆。
位运算符
位运算符是把数字看作二进制来进行计算的。Python 中的按位运算法则如下:
&
:按位与运算符:参与运算的两个值,如果两个相应位都为 1,则该位的结果为1,否则为 0;|
:按位或运算符:只要对应的二个二进位有一个为 1 时,结果位就为 1;^
:按位异或运算符:当两对应的二进位相异时,结果为 1 ;~
:按位取反运算符:对数据的每个二进制位取反,即把 1 变为 0,把 0 变为 1;<<
:左移动运算符:把 “<<” 左边的运算数各二进位全部左移若干位,”<<” 右边的数用来指定移动的位数,高位丢弃,低位补 0;>>
:右移动运算符:把 “>>” 左边的运算数的各二进位全部右移若干位,”>>” 右边的数指定移动的位数。
代码演示:
1 | 60 # 60 = 0011 1100 aNumber = |
比较(关系)运算符
运算符 | 说明 |
---|---|
> | 大于,如果>前面的值大于后面的值,则返回 True,否则返回 False。 |
< | 小于,如果<前面的值小于后面的值,则返回 True,否则返回 False。 |
== | 等于,如果==两边的值相等,则返回 True,否则返回 False。 |
>= | 大于等于(等价于数学中的 ≥),如果>=前面的值大于或者等于后面的值,则返回 True,否则返回 False。 |
<= | 小于等于(等价于数学中的 ≤),如果<=前面的值小于或者等于后面的值,则返回 True,否则返回 False。 |
!= | 不等于(等价于数学中的 ≠),如果!=两边的值不相等,则返回 True,否则返回 False。 |
实例如下:
1 | >> print("89是否大于100:", 89 > 100) |
逻辑运算符
Python 语言支持的逻辑运算符如下:
a and b
:等价于数学中的“且”,a 和 b 两个表达式都真为真,有假即假;a or b
:等价于数学中的“或”,a 和 b 两个表达式有真即真,都假即假;not a
:等价于数学中的“非”,如果 a 为真,那么 not a 的结果为假;如果 a 为假,那么 not a 的结果为真。相当于对 a 取反。
故,逻辑运算符一般和关系运算符结合使用,例如:
1 | >> age = int(input("请输入年龄:")) |
注意,Python 逻辑运算符可以用来操作任何类型的表达式,不管表达式是不是 bool 类型;同时,逻辑运算的结果也不一定是 bool 类型,它也可以是任意类型。
逻辑运算符的本质 >>>>
对于 and 运算符,两边的值都为真时最终结果才为真,但是只要其中有一个值为假,那么最终结果就是假,所以 Python 按照下面的规则执行 and 运算:
如果左边表达式的值为假,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是假,此时 and 会把左边表达式的值作为最终结果;
如果左边表达式的值为真,那么最终值是不能确定的,and 会继续计算右边表达式的值,并将右边表达式的值作为最终结果。
对于 or 运算符,情况是类似的,两边的值都为假时最终结果才为假,只要其中有一个值为真,那么最终结果就是真,所以 Python 按照下面的规则执行 or 运算:
- 如果左边表达式的值为真,那么就不用计算右边表达式的值了,因为不管右边表达式的值是什么,都不会影响最终结果,最终结果都是真,此时 or 会把左边表达式的值作为最终结果。
- 如果左边表达式的值为假,那么最终值是不能确定的,or 会继续计算右边表达式的值,并将右边表达式的值作为最终结果。
使用代码验证上面的结论:
1 | # Python 中,所有的对象都可以进行真假值的测试,包括字符串、元组、列表、字典、对象 |
运行看一下:
1 | ----False and xxx----- |
成员运算符
除了以上的一些运算符之外,Python 还支持成员运算符。正如我们在字符串(str),列表(list)或元组(tuple)、字典(dict)、集合(set)中进行的成员检查。
in
:如果在指定的序列中找到值返回 True,否则返回 False;not in
:如果在指定的序列中没有找到值返回 True,否则返回 False。
关于成员运算符的使用请参考前面字符串(str),列表(list)或元组(tuple)、字典(dict)、集合(set)部分。
身份运算符
身份运算符用于比较两个对象的存储单元:
is
:is 判断两个变量所引用的对象是否相同,如果相同则返回 True,否则返回 False。is not
:is not 判断两个变量所引用的对象是否不相同,如果不相同则返回 True,否则返回 False。
注意,id([object])
函数用于获取对象的内存地址。
下面我们来看身份运算符如何使用:
1 | 20 aNumber = |
三目运算符
除了上面介绍的基本运算符之外,Python 中还支持使用 if else
实现类似于其它编程语言中三目(三元)运算符 ? :
,语法如下:
1 | exp1 if contion else exp2 |
说明 >>>>
condition 是判断条件,exp1 和 exp2 是两个表达式。如果 condition 成立(结果为真),就执行 exp1,并把 exp1 的结果作为整个表达式的结果;如果 condition 不成立(结果为假),就执行 exp2,并把 exp2 的结果作为整个表达式的结果。
看下面的例子:
1 | >> a = 3 |
Python 三目运算符还支持嵌套,如此可以构成更加复杂的表达式。在嵌套时要需要注意 if 和 else 的配对:
1 | a if a>b else c if c>d else d |
但为了保持代码的可读写,建议不要嵌套太多的三元运算符!
运算符优先级
以下给出出了从最高到最低优先级的所有运算符:
【**
:指数 (最高优先级)】–>
【~ + -
:按位取反, 一元加号和减号 (表示正负号)】–>
【* / % //
:乘,除,取模和取整除】–>
【+ -
:加法减法】–>
【>> <<
:右移,左移运算符】– >
【& ^ |
:位与,异或,位或】–>
【<= < > >=
:关系】–>
【== !=
:等于运算符】–>
【= %= /= //= -= += *= **=
:赋值运算符】–>
【is is not
:身份运算符】–>
【in not in
:成员运算符】–>
【and or not
:逻辑运算符】。
Python 中的流程控制结构
和其它编程语言一样,按照执行流程划分,Python 程序也可分为三大结构,即顺序结构、选择(分支)结构和循环结构:
- 顺序结构:让程序按照从头到尾的顺序依次执行每一条 Python 代码,不重复执行任何代码,也不跳过任何代码;
- 选择结构:也称分支结构,就是让程序“拐弯”,有选择性的执行代码;换句话说,可以跳过没用的代码,只执行有用的代码;
- 循环结构,就是让程序“杀个回马枪”,不断地重复执行同一段代码。
分支结构
Python 中,可以使用 if else
语句对条件进行判断,然后根据不同的结果执行不同的代码,这称为 选择结构 或者 分支结构。
分支结构形式
Python 中的 if else 语句可以细分为三种形式,分别是 if 语句
、if else 语句
和 if elif else 语句
:
[1] >>>> 单向判断
单项判断是最简单的分支结构,表示:”如果……就……“,其流程图如下所示:
代码实例如下:
1 | age = 20 |
[2] >>>> 双向判断
双向判断可以帮助我们解决更为复杂的判断,表示:如果……就……,否则的话……,其流程图如下所示:
代码实例如下:
1 | age = 20 |
根据 Python 的缩进规则,如果 if
语句判断是 True
,就把 if
缩进下的两行 print
语句执行了。否则执行 else
下的缩进代码块。
[3] >>>> 多向判断
事实上,上面的判断是很粗略的,完全可以用 if...elif...else...
实现更复杂判断,其流程图如下所示:
Python 会从上到下逐个判断表达式是否成立,一旦遇到某个成立的表达式,就执行后面紧跟的语句块,此时,剩下的代码就不再执行了,不管后面的表达式是否成立。如果所有的表达式都不成立,就执行 else
后面的代码块。
代码结构如下:
1 | if condition_1: |
另外注意,Python 中是没有 switch – case
语句的。
并且当多向判断条件比较多时,可以通过数轴进行包含关系判断,这是很有用的。
if 语句嵌套
上面详细介绍了 3 种形式的条件语句,即 if、if else 和 if elif else,这 3 种条件语句之间可以相互嵌套。例如:
1 | if 表达式1: |
当然,嵌套使用上很灵活。你还可以;
1 | if 表达式1: |
事实上,嵌套的关键在于你要分清楚各判断层的逻辑,由外向内一层层分析,就像剥洋葱一样。
空语句 pass
在实际开发中,有时候我们会先搭建起程序的整体逻辑结构,但是暂时不去实现某些细节,而是在这些地方加一些注释,方面以后再添加代码,请看下面的例子:
1 | age = int( input("请输入你的年龄:") ) |
当年龄大于等于 30
并且小于 50
时,我们没有使用 print() 语句,而是使用了一个注释(#TODO),希望以后再处理成年人的情况。当 Python 执行到该 elif
分支时,会跳过注释,什么都不执行。
Python 提供了一种更加专业的做法,就是空语句 pass
,用来让解释器跳过此处,什么都不做。
就像上面的情况,有时候程序需要 占一个位置,或者放一条语句,但又不希望这条语句做任何事情,此时就可以通过 pass 语句来实现。使用 pass 语句比使用注释更加优雅。如下:
1 | class MyEmptyClass: |
断言函数 assert
assert 语句,又称断言语句,语法结构为:
1 | assert 表达式 |
可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行;反之,Python 解释器会报 AssertionError
错误。
assert 语句的执行流程可以用 if 判断语句表示,如下所示:
1 | if 表达式==True: |
有读者可能会问,明明 assert 会令程序崩溃,为什么还要使用它呢?这是因为,与其让程序在晚些时候崩溃,不如在错误条件出现时,就直接让程序崩溃,这有利于我们对程序排错,提高程序的健壮性。
assert 语句通常用于检查用户的输入是否符合规定,或者限定函数参数类型等。如下:
1 | def func(input): |
循环结构
循环结构应用场景:
每个人的生活和工作都充满了循环,很多时候,循环意味着重复和枯燥:
比如你需要下载很多很多张图片,本来你是要手动操作的,而计算机通过【循环】,就可以依照某些规则,帮你一张一张地下载图片,你在一旁歇着就好。再比如作为运营,可能需要去解散很多的用户群,本来要一个一个手动点击,而计算机通过【循环】,就可以依照某些规则,帮人一个一个解散。
Python 中的【循环语句】,可以让计算机能够重复性地、自动地执行指令。
Python 中提供了常用的两种循环结构支持:
- for … in(For 循环)
- while …(while 循环)
for 循环
[1] >>>> for … in …
Python for
循环可以遍历任何序列或者可迭代(Iterable)的数据元素,如 列表、字符串、元组、字典、集合 等。其一般格式如下:
1 | for <variable> in <sequence/Iterable>: |
例如循环访问列表中元素对象的实例:
1 | "C", "C++", "Perl", "Python"] languages = [ |
一个很形象的 for 循环结构工作流程图 >>>>
1 | for i in [1,2,3,4,5]: |
是不很好理解?
事实上,循环结构就是从一个定义好的元素队列中不断取值,然后完成相应操作,依此类推(循环)的过程。
[2] >>>> for … in … else …
除了上述的结构外,for ... in
语句还可以和 else
联合使用(不常见),构成如下循环结构:
1 | "C", "C++", "Perl", "Python"] languages = [ |
如上,for 循环结束后给出结束信息,以标识循环部分结束。
[3] >>>> for 循环中的 range() 函数的使用
前面在讲解序列的时候,我们提到过 range 也是一种序列结构,事实上,借助它可以生成一个自定义的数字序列。故,如果需要通过 数字序列 实现循环时,还可以使用 Python 内置的 range()
() 函数。例如:
1 | # range(x) 会自动生成一个从 0 到 x-1 的整数序列: |
也可以使用 range() 来限定数字序列区间的取值范围,甚至是步长。如下:
1 | # 生成从 0 到 9,步长为 2 的五个整数组成的序列: |
我们再来看看 len()
&& range()
的组合用法以通过序列索引遍历序列(常见用法),这是有时是很方便的:
1 | "Google", "Baidu", "Opera"] website = [ |
while 循环
[1] >>>> while
先来给出 while 循环的工作流程图:
while 循环表示:满足条件,就按照流程办事。
Python 中 while 语句的一般形式:
1 | while 判断条件: |
循环实例(1~100求和):
1 | 100 n = |
可见,对于 【1~100求和】,只要满足条件【counter <= 100】就累加求和。
[2] >>>> while … else …
和 for ... in
语句一样,while ... else
也可以和 else
,配合使用:
1 | 100 n = |
[3] >>>> while 死循环
没有终止条件,就会导致无限循环(死循环):
1 | while True: |
for…in or while…
考虑一下:这两种循环结构什么场景下都适用么??!
for 循环和 whlie 循环选择的最大的区别在于【循环的工作量是否确定】 >>>>
for 循环就像空房间依次办理业务,直到把【所有工作做完】才下班。但 while 循环就像哨卡放行,【满足条件就一直工作】,直到不满足条件就关闭哨卡。
所以说,当我们【工作量确定】的时候,我们就可以让 for 循环来完成重复性工作。反之,要【工作量不确定时】可以让 while 循环来工作:
1 | # 适合用 for...in... 循环 |
注意,类似于分支结构,for…in 和 while… 循环结构也是支持嵌套的!!!
Break && Continue
当我们在循环任务中,由于达到我们的目的不想再继续循环执行下去时,可以借助 break
&& continue
来进行循环的中止:
[1] >>>> break:跳出当前循环
break
语句可以跳出 for
和 while
的循环体。如果你从 for
或 while
循环中终止,任何对应的循环 else
块将不执行。实例如下:
1 | "Google" Astr = |
可以发现,当存在循环嵌套时,break 只能跳出当前循环体,而外部循环仍在进行。
[2] >>>> continue: 跳过当次循环
continue
语句被用来告诉 Python 解释器跳过当前循环块中的剩余语句,然后继续进行下一轮循环。
1 | 5 flags = |
注意,我们知道循环语句可以有 else 子句,它在穷尽列表( for 循环)或条件变为 false (while 循环)导致循环终止时被执行,但循环被 break
终止时会跳过 else 语句块
不执行。
了解了分支结构以及循环结构,来运用一下吧:
冒泡排序
冒泡排序是数据结构中的经典算法,算法的实现思想遵循以下几步:
- 比较相邻的元素,如果第一个比第二个大,就交换它们两个;
- 从最开始的第一对到结尾的最后一对,对每一对相邻元素做步骤 1 所描述的比较工作,并将最大的元素放在后面。这样,当从最开始的第一对到结尾的最后一对都执行完后,整个序列中的最后一个元素便是最大的数;
- 将循环缩短,除去最后一个数(因为最后一个已经是最大的了),再重复步骤 2 的操作,得到倒数第二大的数;
- 持续做步骤 3 的操作,每次将循环缩短一位,并得到本次循环中的最大数。直到循环个数缩短为 1,即没有任何一对数字需要比较,此时便得到了一个从小到大排序的序列。
例如,使用 for 循环实现用冒泡排序算法对 [5,8,4,1] 进行排序:
1 | data = [5,8,4,1] |
运行结果如下:
1 | 排序后: [1, 4, 5, 8] |
Python 中的推导式
推导式(又称解析器),是 Python 独有的一种特性。
前面在介绍列表、元组、字典以及集合等数据类型的创建时,都给出过使用推导式的创建方法,但只是简单提了一下。
事实上,使用推导式可以快速生成符合条件的列表、元组、字典以及集合类型的数据,因此推导式又可细分为 列表推导式、元组推导式、字典推导式 以及 集合推导式。
推导式统一语法格式如下:
1 | [{( 表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] )}] |
此格式中,具体使用那种括号视数据类型而定;且 [if 条件表达式] 不是必须的,用于进行元素过滤。
列表推导式
列表推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的列表。
列表推导式的语法格式如下:
1 | [表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] ] |
初学者可以这样认为,它只是对 for 循环语句的格式做了一下简单的变形,并用 [] 括起来而已,只不过最大的不同之处在于,列表推导式最终会将循环过程中,表达式计算得到的一系列值组成一个列表。
例如如下代码(程序一):
1 | a_range = range(10) |
还可以在列表推导式中添加 [if 条件语句],过滤符合条件的元素:
1 | # 偶数 |
另外,以上所看到的列表推导式都只有一个循环,实际上它可使用多个循环,就像嵌套循环一样。例如如下代码:
1 | src_a = [30, 12, 66, 34, 39, 78, 36, 57, 121] |
元组推导式
元组推导式可以利用 range 区间、元组、列表、字典和集合等数据类型,快速生成一个满足指定需求的元组。
元组推导式的语法格式如下:
1 | (表达式 for 迭代变量 in 可迭代对象 [if 条件表达式] ) |
通过和列表推导式做对比,你会发现,除了元组推导式是用 () 圆括号将各部分括起来,而列表推导式用的是 [],其它完全相同。不仅如此,元组推导式和列表推导式的用法也完全相同。
例如,我们可以使用下面的代码生成一个包含数字 1~9 的元组:
1 | a = (x for x in range(1,10)) |
输出结果:
1 | <generator object <genexpr> at 0x0000020BAD136620> |
可以看出,使用元组推导式生成的结果并不是一个元组,而是一个生成器对象(后续会介绍),这一点和列表推导式是不同的。
如果我们想要使用元组推导式获得新元组或新元组中的元素,有以下三种方式:
1.使用 tuple() 函数,可以直接将生成器对象转换成元组,例如:
1 | a = (x for x in range(1,10)) |
2.直接使用 for 循环遍历生成器对象,可以获得各个元素,例如:
1 | a = (x for x in range(1,10)) |
3.使用 next() 方法遍历生成器对象,也可以获得各个元素,例如:
1 | a = (x for x in range(3)) |
运行结果为:
1 | 0 |
无论是使用 for 循环遍历生成器对象,还是使用 next() 方法遍历生成器对象,遍历后原生成器对象将不复存在,这就是遍历后转换原生成器对象却得到空元组的原因。
字典推导式
字典推导式可以借助列表、元组、字典、集合以及 range 区间,快速生成符合需求的字典。
字典推导式的语法格式如下:
1 | {表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]} |
可以看到,和其它推导式的语法格式相比,唯一不同在于,字典推导式用的是大括号{}。
【例 1】
1 | listdemo = ['C语言中文网','c.biancheng.net'] |
【例 2】 交换现有字典中各键值对的键和值
1 | olddict={'test': 6, 'test2': 15} |
【例 3】 使用 if 表达式筛选符合条件的键值对
1 | olddict={'test': 6, 'test2': 15} |
集合推导式
集合推导式可以借助列表、元组、字典、集合以及 range 区间,快速生成符合需求的集合。
集合推导式的语法格式如下:
1 | {表达式 for 迭代变量 in 可迭代对象 [if 条件表达式]} |
集合推导式和字典推导式的格式完全相同,那么给定一个类似的推导式,如何判断是哪种推导式呢?最简单直接的方式,就是根据表达式进行判断,如果表达式以键值对(key:value)的形式,则证明此推导式是字典推导式;反之,则是集合推导式。
【例 1】 既然生成的是集合,那么其保存的元素必须是唯一的
1 | tupledemo = (1,1,2,3,4,5,6,6) |
install_url
to use ShareThis. Please set it in _config.yml
.