第三章 程序的结构控制与函数 v1.0
第一部分 结构控制语句
一、简介
先来简单介绍一下程序的三种基本结构:
- 顺序结构:最基本的程序控制结构,默认状态下程序中的各操作按代码的先后顺序依次执行的
- 分支结构:又名选择结构,用于根据条件控制程序走向不同分支
- 循环结构:用于控制程序中经常需要重复执行同一串语句的执行
二、分支(选择)结构
1.基本结构
if 条件表达式: # 条件表达式值为True则执行语句块
语句块
if 条件表达式: # 条件表达式值为True则执行语句块1,否则执行语句块2
语句块1
else:
语句块2
if 条件表达式1: # 条件表达式1值为True则执行语句块1,并跳出分支结构
语句块1
elif 条件表达式2: # 条件表达式2值为True则执行语句块2,并跳出分支结构
语句块2
...
else: # 前面的所有表达式皆为False则执行语句块N
语句块N
# 本例假设grade为某同学成绩
if grade>=90:
print("优秀")
elif grade>=80:
print("良好")
elif grade>=70:
print("中等")
elif grade>=60:
print("及格")
else:
print("不及格")
注意要点:if语句中:
和语句块前的缩进都是语法的一部分,缩进表达语句块与if的包含关系。
2.三目运算符
三目运算符可以理解为二分支结构简洁表达方式,适用于语句块都只包含简单表达式的情况
# 满足条件表达式的情况结果为表达式1的值,不满足条件时结果为表达式2的值
表达式1 if 条件表达式 else 表达式2
# 本例假设grade为某同学成绩
grade = 100
print('及格' if grade>=60 else '不及格') # output: 及格
grade = 50
result = '及格' if grade>=60 else '不及格'
print(result) # output: 不及格
三、循环结构
1.for循环结构
讲for循环之前,需要先讲Python中的两个内置函数——
range()
&enumerate()
①range()函数
它能返回一个可迭代对象,内容是一系列连续增加的整数,参数说明:
start
:计数从start开始(默认为从0开始),range(5)
等价于range(0,5)
stop
:计数到stop结束,但不包括stop(即在stop前一位结束)step
:步长,即每次迭代的增长量,默认为1
range()在Python源码中有两种实现形式如下:
# range(stop) -> range object
range(5) # 0,1,2,3,4
# range(start, stop[, step]) -> range object
range(0,5) # 0,1,2,3,4 => 等价于range(0,5)
range(0,5,3) # 0,3
②enumerate()函数
返回包含可迭代对象索引及可迭代对象本身的迭代器,参数说明:
iterable
:可迭代对象start
:计数从start开始(默认为从0开始)
list(enumerate(["a", "b", "c"])) # [(0, 'a'), (1, 'b'), (2, 'c')]
list(enumerate(["a", "b", "c"], start=1)) # [(1, 'a'), (2, 'b'), (3, 'c')]
③for循环语法
for循环可以理解为可迭代对象中逐一提取元素,在代码块中以循环变量代替迭代中每次对象依次执行一次语句块,for语句的循环执行次数是根据遍历结构中元素个数确定的。
for 循环变量 in 可迭代对象: # 一般是range()函数、字符串或组合数据类型
语句块
for i in range(5):
print(i, end=" ") # output: 0 1 2 3 4
for i,v in enumerate("string"):
print(i, v, end=",") # output: 0 s,1 t,2 r,3 i,4 n,5 g,
for i in [1, 2, 3, 4, 5]:
print(i, end=",") # output: 1,2,3,4,5,
for k,v in {1:10,2:20,3:30}.items(): # items()函数可以取出字典类型中的键值对
print(f"k:{k},v:{v}", end=' ') # output: k:1,v:10 k:2,v:20 k:3,v:30
for k in {1:10,2:20,3:30}: # 字典类型遍历时默认取出其键作为迭代对象
print(f"k:{k}",end=' ') # output: k:1 k:2 k:3
for 循环变量 in 遍历结构:
语句块1
else: # 当for循环正常执行之后,程序才会继续执行else语句中内容
语句块2
2.while循环结构
while 条件表达式: # 条件表达式为True则继续循环体,否则跳出循环体
语句块
i=0
while i<10: # output: 0 1 2 3 4 5 6 7 8 9
print(i, end=" ")
i+=1
while 条件表达式: # 条件表达式为True则继续循环体,否则跳出循环体
语句块1
else:
语句块2
3.循环中断语句
循环结构中的两个辅助循环控制的保留字:break和continue,称为循环中断语句
break
作用:跳出当前循环,之后继续执行循环后代码;有多层循环时,仅跳出最内层循环。continue
作用:结束当次循环,即跳出循环体中下面尚未执行的语句,继续下次循环。
四、异常处理
Python语言使用保留字try和except进行异常处理
try:
语句块1 # 语句块1是执行的程序内容
except 异常:
语句块2 # 发生相应异常时,执行语句块2
else:
语句块3 # 程序无异常产生是,执行语句块3
try:
语句块1
except 异常:
pass # 表示捕获到对应异常时不做任何处理
else:
语句块3
五、推导式
推导式能过滤一个可迭代对象中的元素,并对其进行操作,得到需要的新序列对象。
if条件可以为空,表示不进行过滤只进行对应操作
新列表名 = [表达式 for 循环变量 in 可迭代对象 if 过滤条件] # 列表推导式
新集合名 = {表达式 for 循环变量 in 可迭代对象 if 过滤条件} # 集合推导式
新字典名 = {k:v for 循环变量 in 可迭代对象 if 过滤条件} # 字典推导式
新生成器名 = (表达式 for 循环变量 in 可迭代对象 if 过滤条件) # (了解)生成器推导式
print([x for x in range(5) if x%2==0]) # output: [0, 2, 4]
print({x for x in range(5) if x%2==0}) # output: {0, 2, 4}
print({x:x**2 for x in range(5) if x%2==0}) # output: {0: 0, 2: 4, 4: 16}
print((x for x in range(5) if x%2==0)) # output: <generator object <genexpr> at 0x0000010D242C0E40>
print([x**2 for x in range(5)]) # output: [0, 1, 4, 9, 16]
print({x:x**2 for x in range(5)}) # output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
使用推导式能更加简单快速地对可迭代元素进行操作与转换,简化代码。
第二部分 函数与变量
一、函数的定义与调用
- 函数代码块以
def
关键词开头,后接函数标识符名称和()
包含的参数列表 - 参数列表是调用该函数时传递给它的值,可以有零个、一个或多个,各参数由逗号分隔
- 函数的第一行语句一般使用多行备注书写存放函数说明
- 函数内容以冒号
:
起始,并且缩进 return
语句结束函数并返回值给调用方,无return
语句默认为无返回值的函数
1.语法
def 函数名(参数:参数类型, 可选参数:参数类型 = 默认值, *args, **kwargs) -> 返回值类型:
"""
函数说明
"""
# 函数体
pass # pass充当占位符,表示什么都不做,执行部分思路暂未完成可以使用该语句
return 返回值[列表] # 若返回值为多个值,返回的结果将自动封装为元组类型
def func(): # 这是一个什么都没做的函数
return # 无返回值函数,return在函数体最后也可以不写return
def func():
return 1,2,3
print(func()) # output: (1, 2, 3) => 自动封装为元组类型
函数名(参数列表)
2.函数重载
与C++/Java等语言不同,Python中没有函数重载。
由于Python是一门动态、强类型的解释型语言,不需要声明变量类型但可以通过类型检查和类型转换来实现相似的功能;通过默认参数/可变参数/可变关键字参数可以处理函数参数中参数可变的问题,故没有函数重载的概念但可以实现函数重载的思想。
在Python中,当定义一个同名函数时会覆盖上一个定义的同名函数的内容。
①有默认值的参数
- 函数在定义时可以指定参数默认值,被调用时若未传入参数值,则使用函数的默认值替代。
- 定义函数时,应先给出所有非可选参数,然后再分别列出每个可选参数及对应的默认值。
def 函数名(非可选参数, 可选参数 = 默认值):
"""
函数说明
"""
# 函数体
def add(x, y, is_print: bool = False):
if is_print:
print(f'{x}+{y}={x + y}', end=' ')
return x + y
print(add(1, 1)) # output: 2
print(add(1, 1, is_print=False)) # output: 2
print(add(1, 1, is_print=True)) # output: 1+1=2 2 => 优先使用传入的参数值
②传递任意数量的实参(*args)
- 函数定义时在该形参名前加
*
号,位置放在所有无默认值的形参之后。 - 处理方式:将输入的若干个实参作为元组处理
def 函数名(..., *args): # *args参数放置在必选参数后
"""
函数说明
"""
# 函数体
# 将多个参数作为元组进行处理
def func(*args, x=1, y=2, z=3):
print(f"x={x}, y={y}, z={z}, *args={args}")
func(1, 2, 3) # output: x=1, y=2, z=3, *args=(1, 2, 3)
func(x=4, y=5, z=6) # output: x=4, y=5, z=6, *args=()
func(1, 2, 3, x=4, y=5, z=6) # output: x=4, y=5, z=6, *args=(1, 2, 3)
def my_sum(*args):
sum = 0
for number in args:
sum += number
return sum
print(my_sum(1, 2, 3, 4, 5)) # output: 15
def my_compute(operator="+", *args):
res = args[0]
for number in args[1:]:
res = eval(f"{res}{operator}{number}")
return res
print(my_compute("+", 1, 2, 3, 4, 5)) # output: 15
print(my_compute("*", 1, 2, 3, 4, 5)) # output: 120
③传递任意数量的关键字实参(**kwargs)
- 函数定义时在该形参名前加
**
,位置放在参数列表中所有参数之后。 - 处理方式:将传入的在函数中未定义的若干个实参作为字典处理。
def 函数名(..., **kwargs): # **kwargs参数放置在参数列表最后
"""
函数说明
"""
# 函数体
# 将多个未定义的关键字参数作为字典进行处理
def func(x=1, y=2, z=3, **kwargs):
print(f"x={x}, y={y}, z={z}, **kwargs={kwargs}")
func(a=1, b=2, c=3) # output: x=1, y=2, z=3, **kwargs={'a': 1, 'b': 2, 'c': 3}
func(x=4, y=5, z=6) # output: x=4, y=5, z=6, **kwargs={}
func(a=1, b=2, c=3, x=4, y=5, z=6) # output: x=4, y=5, z=6, **kwargs={'a': 1, 'b': 2, 'c': 3}
3.匿名函数(Lambda表达式函数)
匿名函数是指一类无需定义标识符的特殊函数,主要用途是减少代码的复杂度
匿名函数常结合map()、filter()、reduce()等函数使用
lambda 参数列表:表达式
print((lambda x:x*x)(8)) # output: 64 => 定义了一个匿名函数并使用8作为参数调用
print(list(map(lambda x:x**2, [1, 2, 3, 4, 5]))) # output: [1, 4, 9, 16, 25]
二、变量
变量是一种可以存储各种类型的数据的命名容器,变量名必须是一个合法标识符
我们之前定义的许多内容都是变量,如x=1
中x
就是变量,1
是此时变量中存放的数据
1.局部变量与全局变量
- 局部变量:在函数内部使用的变量,仅在函数内部有效,当函数退出时变量将不再存在。
- 全局变量:在函数之外定义的变量,在程序执行全过程有效。
在函数内部使用全局变量时,需要提前使用保留字global声明,一般形式:global 全局变量名
局部变量与全局变量的关系:
- 局部变量不能在全局作用域中使用
- 一个局部作用域不能使用其他局部作用域内的变量
- 声明为全局变量后,该全局变量可以在局部作用域中读取
- 允许存在名称完全相同的局部变量与全局变量,但不能在同一函数中同时使用
示例如下:
def func(v): # 此时v为局部变量
return v
gl=100 # 此处声明全局变量gl,其值为100
def func1():
global gl # 声明gl为全局变量,此处若不声明则报错(NameError)
return gl
def func2():
gl=200 # 此为与全局变量gl同名的局部变量
return gl
print(f"func1:{func1()},func2:{func2()}") # output: func1:100,func2:200
print(gl) # output: 100 => func2并未修改全局变量gl的值
2.函数的参数传递
Python中函数参数的传递不允许指定采用传值还是引传用,它采用的是传对象引用的方式
传对象引用这种方式相当于传值和传引用的一种综合。
- 若函数收到的为不可变对象(如:数字/元组/...)的引用,不能直接修改原始对象(相当于 "值传递" )
- 若函数收到的为可变对象(如:字典/列表/...)的引用,会修改对象的原始值(相当于 "引用传递" )
- 列表若想只进行"值传递",可以采用传其复制(全切片)或在函数中对其进行复制的方式进行
示例如下:
def func(v:int):
v+=1
print(v)
v=100
func(v) # output: 101
print(v) # output: 100 => 说明v值本身没有改变
def func(v:list[int]):
v[0]=100
print(v)
v=[1,2,3,4,5]
func(v) # output: [100, 2, 3, 4, 5]
print(v) # output: [100, 2, 3, 4, 5] => 说明v值本身发生改变
def func(v:list[int]):
v=v[::] # 函数中对列表进行复制
v[0]=100
print(v)
v=[1,2,3,4,5]
func(v) # output: [100, 2, 3, 4, 5]
print(v) # output: [1, 2, 3, 4, 5] => 说明v值本身没有改变
def func(v:list[int]): # 此处覆盖了上面的func函数
v[0]=100
print(v)
func(v[:]) # output: [100, 2, 3, 4, 5]
print(v) # output: [1, 2, 3, 4, 5] =>说明v值本身发生改变
func(v) # output: [100, 2, 3, 4, 5]
print(v) # output: [100, 2, 3, 4, 5] => 说明v值本身发生改变
3.海象赋值表达式 *Python3.8
Python3.8中新加入了海象赋值表达式,在条件表达式中使用会先赋值,结果再进行比较
def print_number(num):
while (num := num - 1) >= 0:
print(num, end=' ')
print_number(10) # output: 9 8 7 6 5 4 3 2 1 0