Python 流程控制:跳转与中断、if、while、for、try ...(一篇解千疑)

流程控制

简要介绍

在一个朴实无华的程序中,执行总是顺序的,但要实现复杂的逻辑,就必须引入中断和跳转进行流程控制

中断包括故障中断和结束中断,也可以称作是一种跳转。

跳转控制是一个相当成熟的技术,其结构完善且模式多样,几乎在任何一个程序中都不可或缺。包含但不限于:

  • 条件分支结构
  • 循环结构
  • 异常捕获结构
  • 函数调用
  • 中断、流的输入与输出

条件和循环是容易理解的,在汇编的学习中就已经能看到这两种结构的影子了。

异常捕获结构也是一种跳转。典型的,在做爬虫请求时,往往需要不断更换 ip 请求,一个 ip 请求失败带来异常,捕获后再根据需求从 ip池 中拿新的 ip 进行请求。

函数调用也很显然,调用就是要跳转到函数实现部分,其中输入参数和返回结果还涉及堆栈的使用,属于数据存储区的跳转,也并不复杂。

中断常常表现为故障结束程序,也是一种跳转。

流的输入与输出不能在严格意义上称为跳转控制,因为像 an_integral = int(input("输入一个整数:"))只不过是程序停顿下来,等待键入一个值在缓存区,然后获取到赋值给变量。再比如,将一个字符串写入文件中等等实例,都只是在数据的不同区块进行跳转。但是计算机中一切都是数据,代码是、数据是、逻辑也是,所以归纳于此。

总之,流程控制的概念不复杂,下面介绍一些常用结构的语法和案例。


条件结构

if 语句

if 条件语句是流程控制中最常用的语句,用于有条件的执行。

语法

if '条件1' : 		
	'子句体'
(elif '条件n' :	# ()* 表示可以0或多个 elif 子句
	'子句体')*
[else :			# [] 表示可以0或1个 else 子句
	'子句体']
  1. if 根据条件的真假值来判定是否执行:在所有子句体中选择唯一匹配的一个,然后执行该子句体而略过其它。 如果条件均为假值,则 else 子句体如果存在就会被执行,否则直接跳过。
  2. 关键字 elifelse if 的缩写,适用于避免过多的缩进。多个条件判断可以当作其他语言中 switch 语句的替代品(python 中没有该语句)。

案例:根据分数获得等级。

score = int(input("输入你的分数:"))
if score >= 90:
	print("A")
elif score >= 80:
	print("B")
elif score >= 70:
	print("C")
elif score >= 60:
	print("D")
else:
	print("E")

注意:如果要把一个值与多个常量进行比较,或者检查特定类型、属性,match 语句更实用。

match 语句

match 语句是 3.10 版本开始出现的,其要点是对表达式进行模式匹配。根据其内容的完整性,完全可以作为一个匹配结构,但是匹配也是按条件匹配,所以本文将之归纳于此。

语法:一个简明的语法介绍如下,

match '目标表达式':
	(case '匹配模式' [if '条件']:
		'语句块'
	[case _:  # 必定匹配的模块
		'语句块'])+  # [] 表示可存在0或1个, ()+ 表示可存在1或多个
  1. 其中的目标表达式匹配模式都有复杂的语法与规范,更多信息另请参阅 match 语句
  2. [if '条件'] 是一个加强约束的项。

案例:一个简单的匹配个人姓名和年龄的案例。

class Person:
	def __init__(self, name='', age=0):
		self.name = name
		self.age = age

def main():
	persons = [Person('', 0), Person('Ace', 0), Person('Alice', 18)]
	for person in persons:
		match person:
			case Person(name='', ):
				print("无名之辈")
			case Person(name=x, age=0):
				print(f"'{x}'的年龄怎么是'0'呢?")
			case Person(name=x, age=y) if y >= 18:
				print(f"'{y}'岁的'{x}',你已经成年了。")
			case _:
				print("必定的匹配")

if __name__ == '__main__':
	main()

作为一个新语句,这是足够有意思的;另外需要注意的是,matchcase 是弱关键字。

循环结构

循环结构的用武之地往往是遍历与重试。

while 语句

在条件表达式保持为真的情况下重复地执行。

语法

while '条件':
	'循环体'
[else'结束语']  # [] 表示可存在0或1个
  1. 都知道 while 语句是先判断再执行的:最后的 else 子句作为最后的可选项,循环判断为假时执行,往往是 while 循环结构最后的一次操作。
  2. 要注意的是,被 break 结束的循环是不会执行该子句的,这是因为 else 是循环的一部分,break 跳出了循环。所以一般适用于不被 break 结束的有限循环。

** 案例**:角谷定理,输入一个整数,为奇则 ×3+1 为偶则 ÷2,最终会得到 1

num = int(input("输入一个整数:"))
n, i = num, 0
while True:
	if n == 1:
		break
	if n % 2:
		n = n * 3 + 1
	else:
		n = n / 2
	i += 1
print(f"{num}经过'{i}'次角谷迭代后变为一")  # 13, 9

for 语句

for 语句用于对序列(例如字符串、元组或列表)或其他可迭代对象中的元素进行迭代。与 双目运算符 in 联合使用。

语法

for '条目' in '序列':
	'循环体'
[else:
	'结束语']  # [] 表示可存在0或1个
  1. for 循环这里应该开始有迭代可迭代对象迭代器的概念。
  2. 在迭代前,会先根据 '序列' 产生一个可迭代对象,针对该可迭代对象创建一个迭代器。 条目 不断被迭代器赋值迭代,进入循环体,直到结束。
  3. 几乎和 while 循环一样功能的 else 子句,注意相同的问题(即被 break 结束的循环会执行不了该语句)。
  4. for 循环也常被用作 列表推导式元组推导式生成器 等的构造。

案例1:获取 0~100 中的奇数。

arr = [i for i in range(100) if i % 2]

** 案例2**:筛选有效信息。

info = {'name': 'Ace', 'age': 0, 'weight': 121.5, 'height': None, 'school': ''}
useful_info = {}
for key, value in info.items():
	if value:
		useful_info[key] = value
print(useful_value)

跳脱与略过

跳脱是控制循环的一大办法。一个死循环的程序可以通过监视循环次数,次数过多则选择跳脱整个循环(break);循环过程中一些无关紧要的项目可以跳过本轮(continue)。

略过这一说法可能过于生动,但不失其为一种准确的逻辑表达。

continue 语句

跳过一些不予处理的项。

案例:把列表中的偶数×10。

lst = [n for n in range(1, 101)]
for i in range(len(lst)):  # 把列表中的偶数×10
	if lst[i] % 2:
		continue
	lst[i] *= 10
print(lst)

break 语句

跳出最近的一层循环(存在循环嵌套时注意)。

案例:九九乘法表查找。

x, y = input("输入式子如 '2*3' 然后回车:").split('*')
x, y, flag = int(x), int(y), False
for i in range(1, 10):
	for j in range(1, 10):
		if i == x and j == y:
			print("f{x}x{y}={i*j}")
			flag = True
			break
	if flag:
		break

pass 语句

pass 语句不执行任何操作。语法上需要一个语句,但程序不实际执行任何动作时,可以使用该语句。

功能:

  • 临时占位,类似于 省略符…
  • 忽略程序,什么都不执行,但维护程序结构。
class A:
	pass  # 展位

x = 101
if x <= 100:
	pass  # 不执行语句,只维护结构
else:
	print(x, "> 100")

异常捕获结构

任何程序在出现异常时,都有其捕获处理机制。大致来讲,处理分为中断和跳转两种:其一,抛出(raise)异常中断程序(更多详情见Python:异常处理机制),然后开始调试 bug其二,控制指定类型的错误,其出现时跳转到另一个子程序或其它操作。

下面介绍的是,通过尝试try 语句进行异常控制的简要内容。

try 语句

try 语句的句式结构归结为三种。

# 其一
try:
	'尝试区块'
(except ['指定异常' [as '标识符']]: '$1$')+  # 遇到指定异常,执行的区块
[else: '$2$']
[finally: '$3$']
# 其二
try:
	'尝试区块'
(except* '指定异常' [as '标识符']: '$1$')+  # 指定异常组
[else: '$2$']
[finally: '$3$']
# 其三
try:
	'尝试区块'
finally: '最终执行'  # 不可或缺

以一个异常组为案例:

try:
    raise ExceptionGroup("eg",
        [ValueError(1), TypeError(2), OSError(3), OSError(4)])
except* TypeError as e:
    print(f'caught {type(e)} with nested {e.exceptions}')
except* OSError as e:
    print(f'caught {type(e)} with nested {e.exceptions}')
print("over")

第一个TypeError异常组捕获到了 TypeError(2);第二个OSError异常组捕获到了 OSError(3), OSError(4),但是抛出的异常还有一个 ValueError,所以程序会中断,不会输出字符串 over

caught <class 'ExceptionGroup'> with nested (TypeError(2),)
caught <class 'ExceptionGroup'> with nested (OSError(3), OSError(4))
  + Exception Group Traceback (most recent call last):
  |   File "<stdin>", line 2, in <module>
  | ExceptionGroup: eg
  +-+---------------- 1 ----------------
    | ValueError: 1
    +------------------------------------

更多请参阅 Python:异常处理机制

函数结构

函数是一个很庞大的知识,想要要系统熟悉,这里的篇幅是不够的,更多请参阅 Python:函数

这里简要介绍以下函数、匿名函数的定义,案例也相对简单。

函数的定义

python 内部自带许多函数,如 max()min() 等等。一般说到函数定义就是指用户自定义一个函数。自定义函数的语法如下。

语法

['修饰器'] 
def '函数名'(['参数列表']) [-> '返回数据类型']:
	'函数体'
  1. 修饰器有很多种,例如日志修饰器。
  2. 函数名命名规则:见标识符(变量名)命名规则一节。
  3. 函数可以理解为一定有返回值,只不过空函数返回空值。

匿名函数(lambda 表达式) 的定义

lambda 关键字用于创建小巧的匿名函数。lambda a, b: a+b 函数返回两个参数的和。Lambda 函数可用于任何需要函数对象的地方。

在语法上,匿名函数只能是单个表达式。在语义上,它只是常规函数定义的 语法糖。与嵌套函数定义一样,lambda 函数可以引用包含作用域中的变量。

语法

funcname = lambda *args: short_body

函数案例

关于作用域的一个案例

ambda 表达式可以引用包含作用域中的变量

def pow(n):
    return lambda x: n ** x

f = pow(2)
print(f(3))  # 2 ** 3 == 8
print(f(4))  # 2 ** 4 == 16
lambda 表达式返回值作为实参

一个经典的案例就是作为排序的实参!

pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
pairs.sort(key=lambda pair: pair[1])  # 根据英文排序
print(pairs)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值