Skip to content

缩进、条件、循环、迭代

更新: 2025/2/24 字数: 0 字 时长: 0 分钟

这一节我们开始进入到 Python 的结构体中,在里面我们将会学习到如何标识 Python 代码块以及如何使用两种最常用的结构体(条件、循环)。

缩进层级

在大多数编程语言(例如 C/C++ 或 Java )中,通常会使用花括号 {} 来构造一个代码块的语法,然而 Python 使用缩进来标识代码块,这会让代码更加简洁、清晰,并且很容易看出代码块的层级结构和嵌套关系。正因 Python 对缩的依赖比其它语言都强,所以开发者在编写 Python 代码时要格外注意缩进的使用,因为不正确的缩进会触发 IndentationError 缩进错误。具体注意以下两点:

  1. 每级缩进 4 个空格:在 Python 中对缩进的宽度没有明确要求,但建议保持一致性。通常每一级缩进,左侧就要用 4 个空格来表示。如果要缩进两级,左侧就要用 8 个空格来表示

  2. 不建议使用 Tab 制表符:在不同的编辑器中,使用 Tab 制表符所显示宽度可能会不一致(如 2、4 或 8 个字符),从而在代码的阅读和维护时引发问题,这也是 Python 社区和 PEP 8(Python 官方风格指南)推荐使用空格代替 Tab 来表示缩进的原因。当然,如果觉得每次缩进都必须按 4 下空格太麻烦的话,可以在 PyCharm 中设置 1 个 Tab 制表符宽度等于 4 个空格。

image-20240401145833347

提醒

需要补充的是,有些时候我们可能会看到“顶层代码”的说法,所谓的“顶层代码”就是指左侧没有缩进的代码,或者说缩进为 0 个空格的代码。

条件结构体

在程序中需要分情况执行的代码,就可以使用条件结构体。

关键字 if

if 表示如果、假如。使用方法:

  1. if 后面跟条件语句(任何有值的表达式 )。
  2. 若条件语句结果的布尔类型是 True,则执行下面一个缩进的所有代码
  3. 若条件语句结果的布尔类型是 False,则不执行下面一个缩进的所有代码
python
# 关键字True和False可以直接用来作为条件语句,因为if最终判定的就是布尔值。
if True:
	print('Yes!')  # 输出:Yes!
if False:
	print('No!')   # 注释:条件语句的布尔值为False,因此不执行下一个缩进的代码块。
print('执行完毕')   # 输出:执行完毕。注释:最后一个代码语句没有缩进,不属于任何一个if语句中的代码,因此就会继续输出‘执行完毕’。

# if语句可以嵌套多个下级if语句进行判断
age = 20
height = 175
if age > 18:
    if height > 170:
        print('成年人')  # 输出:成年人

# 多层if嵌套可以使用逻辑运算符and进行连接
age = 20
height = 175
if age > 18 and height > 170:
    print('成年人')  # 输出:成年人

警告

需要注意的是,虽然 if 语句下面可以嵌套多个下级 if 语句,但嵌套一般不要超过 3 层,这是因为代码嵌套过深,整体就不够扁平化,会降低代码可读性。

关键字 elif

elif 表示符合这种条件的分支。使用方法:

  1. if 语句里面可以有多个 elif
  2. elif 后面跟条件语句,若符合 elif 条件,那么就执行elif 下面的一个缩进的代码语句。
  3. 即使符合多个 elif 条件,也只会执行最先符合条件的 elif 下面的一个缩进代码语句,后面的 elifelse 都不会执行
python
score = 25
if 100 >= score >= 60:  # 注释:这条分支不满足条件语句,不会进行输出。
    print('及格')
elif 59 >= score >= 0:  # 注释:这条分支最先符合条件语句的,进行了输出。
    print('不及格')      # 输出:不及格。
elif 30 >= score >= 0:  # 注释:这条分支不是最先符合条件语句的,不会进行输出。
    print('再努力')

关键字 else

else 表示其他、另外。使用方法:

  1. else 必须是和 if 一起连用,且一个 if 只能有一个else
  2. else 必须是放在 ifelif 之后使用。
  3. else 作为 ifelif的条件补充,后面不跟条件语句。
  4. 若符合 else 的条件,那么就执行 else 下面的一个缩进的代码语句。
  5. ifelifelse 都存在的情况下,执行路径也只有一条,要么走 if,要么走 elif,要么走 else
python
age = 10
if age >= 60:       # 注释:if判断的是age大于等于60情况。
    print('老年')
elif age >= 18:     # 注释:elif判断的是age小于60大于等于18情况,因为大于等于60一定会走上面的if判断。
    print('成年')
else:               # 注释:else就判断age<18的情况。
	print('未成年')  # 输出:未成年。注释:age的值小于18,因此就走else下面的一个缩进的代码语句进行了输出。

判断布尔值

在上面我们讲 if 条件语句的时候,所判断的就是条件语句结果的布尔值,所以有时候我们会这样写代码:

python
# 随机输入一个数,若为0,则输出空,不为0,则输出值
num = int(input("请输入一个0~5的整数:"))
if num == 0:
    print("空")
else:
    print(num)

前面说过,整型数值 0 的布尔值是 False,其他整型数值的布尔值都是 True,因此我们可以这样写:

python
# 随机输入一个数,若为0,则输出空,不为0,则输出值
num = int(input("请输入一个0~5的整数:"))
if num:
    print(num)
else:
    print("空")

海象运算符

Python 3.8 引入了 := 新特性,因为该符号像一个躺倒的海象(: 像眼睛,= 像尖牙),因此被称为海象运算符(walrus operator)。它用于在表达式中执行赋值后,再对表达式的值进行判断。我们还是以上面的例子举例:

python
# 随机输入一个数,若为0,则输出空,不为0,则输出值(使用海象)
if num := int(input("请输入一个0~5的整数:")):
    print(num)
else:
    print("空")
'''
注释:海象运算符首先会把输入进来的数赋值给num变量,紧接着海象运算符会判断num变量的布尔值,若布尔值为True,执行if下面的代码块,若布尔值为False,执行else下面的代码块。
'''

警告

需要注意的是,不要过度使用海象运算符,以免降低代码的可读性和维护性。

三目运算符

使用格式:值1 if 表达式 else 值2

使用方法:表达式结果的布尔值为 True,返回值是 值1;表达式结果的布尔值为 False,返回值是 值2

python
print(10 if 10 > 20 else 20)  # 输出:20。注释:如果10大于20,结果值为10;如果10小于等于20,结果值为20。

使用三目运算符后,上面的代码又变为了下面这样:

python
# 随机输入一个数,若为0,则输出空,不为0,则输出值
num = int(input("请输入一个0~5的整数:"))
res = num if num else "空"
print(res)

逻辑运算符

前面还说过,在 值1 or 值2 条件中,当 值1 的布尔值为 True 时,返回 值1,当 值1 的布尔值为 False 时,返回 值2。案例如下:

python
print(0 or 1)  # 输出:1

使用逻辑运算符后,上面的代码还可以这样来写:

python
# 随机输入一个数,若为0,则输出"空",不为0,则输出值
num = int(input("请输入一个0~5的整数:"))
res = num or "空"
print(res)

循环结构体

在程序中需要重复执行的代码,就可以使用循环结构体。

for 循环

for 循环通常用在循环次数确定或者遍历一个可迭代对象情况下。代码格式如下:

python
for 变量名 in 可迭代对象:
	循环体

# for:表示for循环的声明
# 变量名:新的数据存储容器(若不想使用变量可用下划线_代替)
# in:表示从...里面取
# 可迭代对象:实现了迭代协议的对象。
# 循环体:需要重复执行的代码块
# 执行过程:使用变量去可迭代对象中遍历数据,每遍历一个值就执行一次循环体,直到遍历完为止。

建议

关于可迭代对象的详细内容会在后面讲到,这里可以先把它看作一种对象的类型,就例如字符串类型对象(例如,abcd)那样。

range 函数

range 函数是 Python 自带的一个可以生成指定范围数字序列、控制循环次数或者产生索引值的函数,其最终返回结果是一个可迭代对象,因此可以用在 for 循环当中。使用格式如下:

  • range(m)m 必须是整型,产生 0 ~ m-1 的整数序列,取不到 m

  • range(m, n)mn 都必须是整型,如果 n > m 则产生 m ~ n-1 的整数序列,取不到 n;如果 n <= m 则不会有任何的输出。

  • range(m, n, step)mnstep 都必须是整型,其中 step 表示步长,可自定义,也可省略不写,其默认值 1,但不能等于 0 。从 m 开始每次增加 step 直到 n-step 值为止;如果 n > mstep 的值必须为正整数,才会有输出;如果 n < mstep 的值必须为负数,才会有输出。

python
for x in range(10):         # 注释:range(10)是一个0~10前闭后开的可迭代对象,通过变量x遍历。
    print(x, end=' ')       # 输出:0 1 2 3 4 5 6 7 8 9

for x in range(1, 10):      # 注释:range(1, 10)是一个1~10前闭后开的可迭代对象,通过变量x遍历。
    print(x, end=' ')       # 输出:1 2 3 4 5 6 7 8 9

for x in range(1, 10, 2):   # 注释:range(1, 10, 2)是一个1~10前闭后开步长为2的可迭代对象。
    print(x, end=' ')       # 输出:1 3 5 7 9

for x in range(1, 10, -1):  # 注释:在m小于n的情况下,步进必须是正整数,所以这里是一种错误的使用方式。
    print(x, end=' ')

for x in range(10, 1, -1):  # 注释:range(10, 1, -1)是一个10~1前闭后开步长为-1的可迭代对象。
    print(x, end=' ')       # 输出:10 9 8 7 6 5 4 3 2

使用 for 循环来求 1 ~ 100 的和,代码案例如下:

python
"""单层 for 循环"""
n = 99
res = 0
# 循环求和 1, 2, ..., 99, 100
for i in range(1, n + 1):
    res += i
print(res)  # 输出:4950

求和函数的流程框图

建议

此求和操作的操作数量与输入数据大小 nn 成正比,或者说成“线性关系”。

我们可以在一个循环结构内嵌套另一个循环结构,下面以 for 循环为例:

python
"""双层 for 循环"""
n = 3
res = ""
# 循环 i = 1, 2, ..., n-1, n
for i in range(1, n + 1):
    # 循环 j = 1, 2, ..., n-1, n
    for j in range(1, n + 1):
        res += f"({i}, {j}), "
print(res)  # 输出:(1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3),

嵌套循环的流程框图

建议

在这种情况下,函数的操作数量与 n2n^2 成正比,或者说算法运行时间和输入数据大小 nn 成“平方关系”。我们可以继续添加嵌套循环,每一次嵌套都是一次“升维”,将会使时间复杂度提高至“立方关系”、“四次方关系”,以此类推。

while 循环

for 循环不同的是,while 循环每次都会先检查条件,如果条件为真,则继续执行,否则就结束循环,因此常用在循环次数不确定情况下。代码格式如下:

python
while 条件语句:
   循环体

# while:表示while循环的声明。
# 条件语句:布尔类型结果是True或者False。
# 循环体:需要重复执行的代码段。
# 执行过程:只要条件语句结果为True就执行循环体,直到条件语句的值为False,就不再执行循环体,循环结束。

在使用 while 循环时,循环体中要有结束循环的操作,否则就会造成死循环。代码案例如下:

python
# 注释:在条件语句为True,循环体没有结束循环的操作,这就是一个死循环。
while True:
	print('aaa')

# 注释:新增一个flag变量,在满足某种条件下来跳出循环,这样可以优雅应对死循环。
flag = True
while flag:
    print('aaa')
    flag = False
'''
输出:aaa
注释:变量flag第一次赋值为True,while循环条件成立,执行下面的循环体,输出'aaa',变量flag第二次赋值为False,while循环条件不成立,不执行循环体
'''

while 循环比 for 循环的自由度更高。在 while 循环中,我们可以自由地设计条件变量的初始化和更新步骤。代码案例如下:

python
n = 100
res = 0
i = 1  # 初始化条件变量
while i <= n:
    res += i
    i += 1  # 更新条件变量
print(res)

建议

总的来说,for 循环的代码更加紧凑,while 循环更加灵活,两者都可以实现迭代结构。选择使用哪一个应该根据特定问题的需求来决定。

关键字 continue

continue 表示结束本次循环,进入下次循环,可用在 for 循环、while 循环中。

python
for x in range(5):
    if x % 2:          # 注释:筛选出奇数
        continue       # 注释:结束这次循环,进入下次循环。
    print(x, end=' ')  # 输出:0 2 4

关键字 break

break 表示结束整个循环,可用在 for 循环、while 循环中。

python
while True:
    print(0)  # 先输出:0
    break     # 注释:遇到break直接结束整个循环
print('结束')  # 后输出:结束

for else 结构

关键字 else 不仅可以与关键字 if 搭配使用,还可以与 for 循环体结合形成 for else 结构。这个结构允许你在 for 循环正常结束(即没有通过 break 语句跳出)时执行一些额外的代码。主要有以下两个用途:

  • 检测 for 循环中是否执行了 break 进行了跳出。
  • 在一些场景上可以减少代码量,例如在搜索特定元素时,如果循环未找到可以执行 else,否则使用 break 提前结束,这样就可以避免使用 flag

首先必须在 for 循环正常结束后,else 中的语句才会被执行:

python
for i in range(1):
    continue
else:
    print("所有循环走完,正常结束!")  # 输出:所有循环走完,正常结束!

哪怕是在 for 循环的最后一步,使用 break 跳出,也不会执行 else 中的语句:

python
for i in range(1):
    break
else:
    print("所有循环走完,正常结束!")  # 注释:上面执行了break跳出,因此这里不会有任何输出。

while else 结构

除了 for else 结构,还有 while else 结构,其功能和 for else 类似。这个结构允许你在 while 循环正常结束(即没有通过 break 语句跳出)时执行一些额外的代码。下面是一个简单的示例:

python
count = 0
while count < 5:
    print(f"这是第 {count + 1} 次循环")  
    count += 1  
else:  
    print("循环正常结束")  # 输出:循环正常结束。注释:while循环会执行5次。当count达到5时,循环条件不再满足,因此循环正常结束。此时,else块中的代码会被执行,输出 "循环正常结束"。

需要注意的是,如果 while 循环中使用了 break 语句导致循环提前结束,那么 else 块中的代码将不会被执行。例如:

python
count = 0  
while count < 5:
    print(f"这是第 {count + 1} 次循环")  
    count += 1  
    if count == 3:  
        break  # 注释:当count达到3时,break语句会被执行,导致循环提前结束。因此,else块中的代码不会被执行,也就不会输出"循环正常结束"。
else:  
    print("循环正常结束")

可迭代对象(Iterable

可迭代对象(Iterable)指实现了迭代协议的对象,这个“迭代协议”后面会细讲,我们可以先理解为对象的遍历功能,它强调的是通过 for 循环等方式逐个访问可迭代对象中的每个元素,以便进行特定的操作或处理,在数据处理和算法实现时经常会用到

判定方法

上面我们提到 range 函数最后的返回值是可迭代对象,我们可以使用如下方法进行验证,顺便还可以验证一下,前面我们所接触的对象是否是可迭代对象,代码如下:

python
# 从collections.abc库导入Iterable可迭代对象(若Python版本较低,则引入collections库)
from collections.abc import Iterable

# isinstance(对象, Iterable):判断一个对象是否为可迭代对象。
print(isinstance(range(1), Iterable))  # 输出:True。注释:range函数返回的是一个可迭代对象。
print(isinstance(1234, Iterable))      # 输出:False
print(isinstance(1.234, Iterable))     # 输出:False
print(isinstance('1', Iterable))       # 输出:True。注释:字符型数据是可迭代对象。
print(isinstance('1234', Iterable))    # 输出:True。注释:字符型数据是可迭代对象。
print(isinstance(True, Iterable))      # 输出:False
print(isinstance(False, Iterable))     # 输出:False

从上面验证的例子中可以看到,除了 range 函数对象外,字符型数据也是一个可迭代对象,也就是说字符型数据可以直接用于 for 循环当中。例子如下:

python
for char in '123':
    print(char, end=', ')
print(char)
'''
输出:1, 2, 3, 3
注释:新建了一个空的变量char,通过它取字符串123的数据,每次取一个值就保存到变量char,执行下面的输出语句,输出变量的值,字符串的长度是3,输出语句就依次输出三个值。变量char每次接收新的值都会覆盖前面的值,因此for循环结束后,再输出变量char的值就是最后一次保存的值。
'''

建议

在后面我们还会深入学习可迭代对象。另外,可迭代对象可不止 rang() 函数对象、字符型对象这两种,后面会讲到其它的数据类型,它们有些也是可迭代对象。

成员运算

前面我们学习的成员运算符(in/not in)可以用来判断某个元素是否存在于可迭代对象当中,用法如下:

  • 元素 in 可迭代对象 判断元素存在于可迭代对象中,结果为布尔值。
  • 元素 not in 可迭代对象 判断元素不存在于可迭代对象中,结果为布尔值。
python
print('a' in 'abc')       # 输出:True。注释:'a'存在于可迭代对象'abc'中。
print('1' not in 'abc')   # 输出:True。注释:'1'不存在于可迭代对象'abc'中。
print(0 in range(4))      # 输出:True。注释:0存在于可迭代对象range(4)中。
print(5 not in range(4))  # 输出:True。注释:5不存在于可迭代对象range(4)中。

内置函数

在 Python 有许多内置函数,它们的参数要求必须是可迭代对象,例举如下:

  • len(可迭代对象) 返回可迭代对象的长度。
python
print(len('汉字'))     # 输出:2。注释:每个汉字在len函数中的长度为1。
print(len('1234'))    # 输出:4
print(len(range(4)))  # 输出:4。注释:rang(4)返回了一个长度为4的可迭代对象。
  • min(可迭代对象)/max(可迭代对象) 返回可迭代对象中的数值或编码最小/最大的元素。
python
print(min('abcZ'))    # 输出:Z。注释:大写字母Z的编码。在'abcZ'中最小。
print(min(range(4)))  # 输出:0。注释:在rang(4)可迭代对象中最小值为0。
print(max('abcZ'))    # 输出:c。注释:小写字母c的编码。在'abcZ'中最大。
print(max(range(4)))  # 输出:3。注释:在rang(4)可迭代对象中最大值为3。
  • sum(可迭代对象) 返回可迭代对象中的数值之和,注意可迭代对象里面的元素必须全是整型或浮点型。
python
print(sum('012345'))  # 报错:'012345'虽然是可迭代对象,但对象里面的元素不是整型或浮点型。
print(sum(range(5)))  # 输出:10。注释:输出range(5)对象中的数字之和。