控制结构与函数
控制结构与函数
3.1 分支结构
3.1.1 单分支结构
Python 的单分支结构使用 if 保留字对条件进行判断,使用方式如下:
if <条件>:
<语句块>
<语句块> 是 if 条件满足后执行的一个或多个语句序列,缩进表达 <语句块> 与 if 的包含关系。<条件> 是一个产生 True 或 False 结果的语句,当结果为 True 时,执行 <语句块>,否则跳过 <语句块>。
3.1.2 二分支结构
Python 的二分支结构使用 if-else 保留字对条件进行判断,语法格式如下:
if <条件>:
<语句块 1>
else:
<语句块 2>
<语句块 1> 在 if 中 <条件> 满足即为 True 时执行,<语句块 2> 在 if 中 <条件> 不满足即为 False 时执行。简单来说,二分支结构根据条件的 True 或 False 结果产生两条路径。
二分支结构还有一种更简洁的表达方式,适合 <语句块 1> 和 <语句块 2> 都只包含简单表达式的情况,语法格式如下:
<变量> = <表达式 1> if <条件> else <表达式 2>
1
如果 <条件> 为 True,则将 <表达式 1> 的结果赋值给 <变量>,否则将 <表达式 2> 的结果赋值给 <变量>。
first_num = eval(input(‘请输入*个数:’))
second_num = eval(input(‘请输入第二个数:’))
bigger_num = first_num if first_num >= second_num else second_num
print(f’较大的数是{bigger_num}。’)
————————————————————————
请输入*个数:50
请输入第二个数:25
较大的数是50。
3.1.3 多分支结构
Python 的 if-elif-else 用于描述多分支结构,语句格式如下:
if <条件 1>:
<语句块 1>
elif <条件 2>:
<语句块 2>
…
else:
<语句块 n>
多分支结构通常用于判断同一个条件或一类条件的多个执行路径。Python 会按照多分支结构的代码顺序依次评估判断条件,寻找并执行*个结果为 True 的条件所对应的语句块。当 if-elif-else 中的某个语句块执行后,Python 就会跳过整个 if-elif-else 结构。即使后续的条件测试为真,其对应的语句块也不会被执行。
此外,Python 并不要求 if-elif 结构后面的代码必须有 else 代码块。else 代码块是一个包罗万象的语句,只要不满足任何 if 或 elif 中的条件测试,else 中的代码就会被执行。这可能会引入无效甚至恶意的数据。如果知道*终要测试的条件,应考虑使用一个 elif 代码块代替 else 代码块。这样就可以肯定,仅当满足相应的条件时,代码才会执行。
3.1.4 条件判断及组合
分支结构中的条件判断可以使用任何能够产生 True 或 False 的语句或函数。形成判断条件*常见的方式是采用关系操作符:<、<=、>、>=、==、!=。
Python 语言中,任何非零的数值、非空的数据类型都等价于 True,0 或空类型等价于 False,它们可以直接用作判断条件。
Python 语言使用保留字 not、and 和 or 对条件进行逻辑运算或组合。保留字 not 表示单个条件的“否”关系,and 表示多个条件之间的“与”关系,保留字 or 表示多个条件之间的“或”关系。
3.1.5 测试多个条件
if-elif-else 结构功能强大,但仅适用于只有一个条件满足的情况:遇到了通过了的测试后,Python 便会跳过余下的测试。这种设定效率很高,但只能测试一个特定的条件。
如果需要检查多个条件,应使用一系列不包含 elif 和 else 代码块的简单 if 语句。在可能有多个条件为 True 且需要在每个条件为 True 时都采取响应措施时,这种方法将非常适合。
3.2 循环结构
3.2.1 遍历循环
遍历循环可以理解为从遍历结构中逐一提取元素,放在循环变量中,对于每个所提取的元素执行一次语句块。for 语句的循环执行次数是根据遍历结构中元素个数确定的。其语法结构如下:
for <循环变量> in <遍历结构>:
<语句块>
<遍历结构> 可以是字符串、range() 函数或组合数据类型、文件等。对于字符串,可以逐一遍历字符串的每个字符,基本使用方式如下:
for <循环变量> in <字符串变量>:
<语句块>
使用 range() 函数,可以指定语句块的循环次数,基本使用方式如下:
for <循环变量> in range(<循环次数>):
<语句块>
除了在 range() 函数里直接指定循环次数,也可以使用如下用法:range(start, stop[, step])。表示计数从 start 开始,到 stop 结束,但不包括 stop。step 为步长,缺省时步长为 1。
3.2.2 无限循环
Python 通过保留字 while 实现无限循环:
while <条件>:
<语句块>
当程序执行到 while 语句时,条件测试如果为 True,执行循环体语句,语句结束后返回再次判断 while 语句的条件;当条件测试为 False 时,循环终止,执行与 while 同级别缩进的后续语句。
while 定义的是无限循环,让其在恰当的时候停止运行是很重要的。如果让 while 循环停止的条件不多时,通过简单的条件测试即可结束它的运行。但在更复杂的程序当中,很可能会有众多的事件将导致循环的停止,在这种情况下如果在一条 while 语句中检查所有的这些条件,将既复杂又困难。
一种很好的方法是定义一个变量将其作为标志(flag),用于判断整个程序是否处于活动状态。它相当于程序的交通信号灯,当所有的条件满足时,它的值为 True,循环继续执行;一旦循环条件不满足时就为 False,以此来结束程序的运行。
prompt = “\n我是一个复读机。”
prompt += “\n我会重复你的话,输入 quit 结束程序。”
active = True
while active:
message = input(prompt)
if message == ‘quit’:
active = False
else:
print(message)
————————————————————————
我是一个复读机。
我会重复你的话,输入 quit 结束程序。你好
你好
我是一个复读机。
我会重复你的话,输入 quit 结束程序。你是只鹦鹉
你是只鹦鹉
我是一个复读机。
我会重复你的话,输入 quit 结束程序。quit
上述程序中,变量 active 被设置成了标志位,用于判断输入的内容是不是 quit 以确定程序是否继续执行。
3.2.3 循环控制
循环结构有两个辅助循环控制的保留字:break 和 continue。
break 用来跳出 for 或 while 循环,脱离该循环后程序从循环后的代码继续执行。如果有 2 层或多层循环,break 只能退出*内层循环。
利用 break 可将上述代码改写:
prompt = ‘\n我是一个复读机。’
prompt += ‘\n输入 quit 结束程序。’
while True:
message = input(prompt)
if message == ‘quit’:
break
else:
print(message)
————————————————————————
我是一个复读机。
我会重复你的话,输入 quit 结束程序。你好
你好
我是一个复读机。
我会重复你的话,输入 quit 结束程序。你是只鹦鹉
你是只鹦鹉
我是一个复读机。
我会重复你的话,输入 quit 结束程序。quit
continue 用来结束本次循环,即跳出循环体中下面尚未执行的语句,但不跳出当前循环。
current_number = 0
while current_number < 10:
current_number += 1
if current_number % 2 == 0:
continue
print(current_number)
————————————————————————
continue 语句和 break 语句的区别是:continue 语句只结束本次循环,不终止整个循环的执行,而 break 具备结束循环的能力。
3.2.4 循环中的 else 子句
循环语句支持 else 子句。for 循环中,可迭代对象中的元素全部循环完毕时,或当 while 循环的条件为假时,执行该子句;break 语句终止循环时,不执行该子句。
for i in range(10):
print(i, end=’ ‘)
else:
print(‘循环结束。’)
————————————————————————
0 1 2 3 4 5 6 7 8 9 循环结束。
for i in range(10):
print(i, end=’ ‘)
if i == 5:
break # break终止了循环,不执行else子句
else:
print(‘循环结束。’)
————————————————————————
0 1 2 3 4 5
num = 9
while num >= 0:
print(num, end=’ ‘)
num -= 1
else:
print(‘循环结束。’)
————————————————————————
9 8 7 6 5 4 3 2 1 0 循环结束。
num = 0
while num >= 10:
print(num, end=’ ‘)
num -= 1
else:
print(‘循环结束。’)
————————————————————————
循环结束。
3.3 函数的基本使用
3.3.1 函数的概念
函数是一段具有特定功能的、可重用的代码块,通过函数名来表示和调用。经过定义,一组语句等价于一个函数,在需要使用这组语句的地方,直接调用函数名称即可。
Python 定义一个函数使用 def 保留字,语法形式如下:
def <函数名>(<参数列表>):
<函数体>
return <返回值列表>
函数名可以是任何有效的 Python 标识符。参数列表是调用该函数时传递给它的值,可以有零个、一个或多个。当传递多个参数时各参数由逗号分隔,没有参数时也要保留圆括号。函数体是函数每次被调用时执行的代码,由一行或多行语句组成。
如果需要返回值,使用保留字 return 和返回值列表。函数可以没有 return 语句,表示函数没有返回值。函数体运行结束后会将控制权返回给调用者。
3.3.2 基本用法
定义后的函数不能直接运行,需要经过调用才能运行。调用函数的基本方法如下:
<函数名>(<实际赋值参数列表>)
1
具体来说,函数的使用一共分为四个步骤:函数定义、函数调用、函数执行、函数返回。
函数定义:使用 def 保留字将一段代码定义为函数,需要确定函数的名字、参数的名字、参数的个数,使用参数名称作为形式参数(占位符)编写函数内部的功能代码。
函数调用:通过函数名调用函数功能,对函数的各个参数赋予实际值,实际值可以是实际数据,也可以是在调用函数前已经定义过的变量。
函数执行:函数被调用后,使用实际参数(赋予形式参数的实际值)参与函数内部代码的运行。
函数返回:函数执行结束后,根据 return 保留字的指示决定是否返回结果。如果返回结果,则结果将被放置到函数被调用的位置。函数使用完毕,程序继续运行。
3.3.2 传递实参
向函数传递实参的方式很多。可使用位置实参,这要求实参的顺序与形参的顺序相同;也可用关键字实参,其中每个实参都由变量名和值组成;还可以向函数传递列表和字典。
调用函数时,Python 必须将函数调用中的每个实参都关联到函数定义中的形参。为此,*简单的关联方式是基于实参的顺序。这种关联方式称为位置实参。
def describe_pet(animal_type, pet_name):
“””显示宠物信息。”””
print(f”\nI have a {animal_type}.”)
print(f”My {animal_type}’s name is {pet_name.title()}.”)
describe_pet(‘parrot’, ‘polly’)
————————————————————————
I have a parrot.
My parrot’s name is Polly.
位置实参的顺序很重要,如果实参的顺序不正确,结果可能会出乎意料。
关键字实参则是传递给函数的名称值对。因为直接在实参中将名称和值关联起来,所以向函数传递实参时不会混淆。关键字实参让你无须考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值得用途。
def describe_pet(animal_type, pet_name):
“””显示宠物信息。”””
print(f”\nI have a {animal_type}.”)
print(f”My {animal_type}’s name is {pet_name.title()}.”)
describe_pet(animal_type=’parrot’, pet_name=’polly’)
————————————————————————
I have a parrot.
My parrot’s name is Polly.
3.3.3 返回简单值
return 语句用来结束函数并将程序返回到函数被调用的位置继续执行。return 语句可以出现在函数中的任何部分,当执行 return 语句时,即使函数主体中还有其他语句,函数执行也会停止,同时可以将 0 个、1 个或多个函数运算的结果返回给函数被调用处的变量。
函数可以没有 return,此时函数并不返回值。当函数使用 return 返回多个值,应该使用一个变量或多个变量保存结果。如果使用一个变量接收多个返回值的话,Python 将把多个返回值以元组的形式赋值给该变量。
def caluculate(x, y=10):
“””返回两个参数相乘和相加的结果。”””
return x*y, x+y
s = caluculate(99, 2)
print(s)
a, b = caluculate(99, 2)
print(a)
print(b)
————————————————————————
(198, 101)
198
101
3.3.4 为形参指定默认值
函数的参数在定义时可以指定默认值,当函数被调用时,如果没有传入对应的参数值,则使用函数定义时的默认值替代。函数定义时的语法形式如下:
def <函数名>(<非可选参数列表>, <可选参数>=<默认值>):
<函数体>
return <返回值列表>
1
2
3
使用默认值时,必须先在形参列表中列出没有默认值的形参,再列出有默认值的实参。这样才能让 Python 正确地解读位置实参。
def describe_pet(pet_name, animal_type=’dog’):
“””显示宠物信息。”””
print(f”\nI have a {animal_type}.”)
print(f”My {animal_type}’s name is {pet_name.title()}.”)
describe_pet(pet_name=’willie’)
————————————————————————
I have a dog.
My dog’s name is Willie.
3.3.5 让实参变成可选的
有时候,需要让实参变成可选的,这样使用函数的人就能只在必要的时候提供额外的信息。可使用默认值来让实参变成可选的。
例如要处理一位西方人的姓名,他们的姓名都有 first name 与 last name,但并非每个人的名字都有 middle name。下面的程序便演示了怎样处理这个问题:
def get_formatted_name(first_name, last_name, middle_name=”):
“””返回整洁的姓名。”””
if middle_name:
full_name = f”{first_name} {middle_name} {last_name}”
else:
full_name = f”{first_name} {last_name}”
return full_name.title()
musician = get_formatted_name(‘michael’, ‘jackson’)
print(musician)
musician = get_formatted_name(‘michael’, ‘jackson’, ‘joseph’)
print(musician)
————————————————————————
Michael Jackson
Michael Joseph Jackson
3.4 更多的函数使用细节
3.4.1 等效的函数调用
Python 支持混合使用位置参数、关键字参数和默认值,因此有多种等效的函数调用方式。例如下面对函数 describe_pet() 的定义中,给一个形参提供了默认值:
def describe_pet(pet_name, animal_type=’dog’):
1
基于这种定义,在任何情况下都必须给 pet_name 提供实参,指定该实参时可采用位置方式,也可采用关键字方式。如果要函数描述的动物不是小狗,还必须在函数调用中给 animal_type 提供实参。同样,指定该实参时可以采用位置方式,也可采用关键字方式。
下面对这个函数的调用方式都可行:
# 一条名为 Willie 的小狗
describe_pet(‘willie’)
describe_pet(pet_name=’willie’)
# 一只名为 Polly 的鹦鹉
describe_pet(‘polly’, ‘parrot’)
describe_pet(pet_name=’polly’, animal_type=’parrot’)
describe_pet(animal_type=’parrot’, pet_name=’polly’)
3.4.2 返回字典
函数可返回任何类型的值,包括列表和字典等比较复杂的数据结构。下面的函数接受姓名的组成部分,并返回一个表示个人信息的字典:
def build_person(first_name, last_name, age=None):
“””返回一个字典,其中包含有关一个人的信息。”””
person = {‘first’: first_name, ‘last’: last_name}
if age:
person[‘age’] = age
return person
musician = build_person(‘michael’, ‘jackson’)
print(musician)
musician = build_person(‘michael’, ‘jackson’, age=’50’)
print(musician)
————————————————————————
{‘first’: ‘michael’, ‘last’: ‘jackson’}
{‘first’: ‘michael’, ‘last’: ‘jackson’, ‘age’: ’50’}
在函数定义中,定义了一个可选形参 age,其默认值设置成了 None,表示变量没有值。可以将 None 视为占位符,在条件测试中,None 相当于 False。如果函数调用中包含形参 age 的值,这个值也将被存储到字典中。
3.4.3 结合使用函数和 while 循环
下面的程序使用函数 get_formatted_name() 和 while 循环,以更正式的方式问候用户。同时,该程序提供了退出的方式。
def get_formatted_name(first_name, last_name):
“””返回整洁的姓名。”””
full_name = f”{first_name} {last_name}”
return full_name.title()
while True:
print(“\nPlease tell me your name:”)
print(“(enter’q’at any time to quit)”)
f_name = input(“First name:”)
if f_name == ‘q’:
break
l_name = input(“Last name:”)
if l_name == ‘q’:
break
formatted_name = get_formatted_name(f_name, l_name)
print(f”\nHello, {formatted_name}!”)
————————————————————————
Please tell me your name:
(enter ‘q’ at any time to quit)
First name: john
Last name: lennon
Hello, John Lennon!
Please tell me your name:
(enter ‘q’ at any time to quit)
First name: michael
Last name: jackson
Hello, Michael Jackson!
Please tell me your name:
(enter ‘q’ at any time to quit)
First name: q
3.4.4 列表的传递
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的。假设有一家为用户提交的设计制作 3D 打印模型的公司,需要将打印的设计存储到一个列表中,打印后将移到另一个列表中。下面的程序展示了这一实现:
def print_models(unprinted_designs, completed_models):
“””
模拟打印每个设计,直到没有未打印的设计为止。
打印每个设计后,都将其移到列表 completed_models 中。
“””
while unprinted_designs:
current_design = unprinted_designs.pop()
print(f”Printing model: {current_design}”)
completed_models.append(current_design)
def show_completed_models(completed_models):
“””显示打印好的模型。”””
print(“\nThe following models have been printed:”)
for completed_model in completed_models:
print(completed_model)
unprinted_designs = [‘phone case’, ‘robot pendant’, ‘dodecahedron’]
completed_models = []
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
————————————————————————
Printing model: dodecahedron
Printing model: robot pendant
Printing model: phone case
The following models have been printed:
dodecahedron
robot pendant
phone case
有时候,需要禁止函数修改列表。例如在上面的程序中,如果需要保留原来的未打印的设计列表以供备案,那么就不应该让函数修改列表。为了实现这个目的,可以向函数传递列表的副本而非原件。要将列表的副本传递给函数,可以使用列表的切片表示法:
print_models(unprinted_designs[:], completed_models)
1
不过,给函数传递列表副本将会降低程序运行效率,尤其是在处理大型列表时。因此除非有充足的利用,否则还是应该将原始列表传递给函数。
3.4.5 传递任意数量的实参
有时候,预先不知道函数需要接受多少个实参,不过好在 Python 允许函数从调用语句中收集任意数量的实参。
下面是一个模拟制作比萨的程序,形参名 * toppings 中的星号表示创建一个名为 toppings 的空元组。此外,这个程序结合使用了位置实参和任意数量的实参。为了让函数正确接收参数,应当把接收任意数量实参的形参放到参数列表的*后。
def make_pizza(size, *toppings):
“””概述要制作的比萨。”””
print(f”\nMaking a {size}-inch pizza with the following toppings:”)
for topping in toppings:
print(f”- {topping}”)
make_pizza(16, ‘pepperoni’)
make_pizza(12, ‘mushrooms’, ‘green peppers’, ‘extra cheese’)
————————————————————————
Making a 16-inch pizza with the following toppings:
– pepperoni
Making a 12-inch pizza with the following toppings:
– mushrooms
– green peppers
– extra cheese
在上面的程序中,函数需要接收任意数量的实参,但尚且知道接受的是怎样的信息:比萨的尺寸及配料。有时,函数不仅需要接受任意数量的实参,而且预先也不知道传递给函数的会是怎样的信息。在这种情况下,可将函数编写成能接受任意数量的键值对——调用语句提供了多少就接受多少。下面的程序演示了创建用户的简介:
def build_profile(first, last, **user_info):
“””创建一个字典,其中包含获悉的有关用户的一切。”””
user_info[‘first_name’] = first
user_info[‘last_name’] = last
return user_info
user_profile = build_profile(‘albert’, ‘einstein’,
location=’princeton’,
field=’physics’)
print(user_profile)
————————————————————————
{‘location’: ‘princeton’, ‘field’: ‘physics’, ‘first_name’: ‘albert’, ‘last_name’: ‘einstein’}
上述程序中的函数要求必须提供名和姓,同时允许调用者根据需要提供任意数量的名称值对。形参 **user_info 中的两个星号表示创建一个名为 user_info 的空字典,并将收到的所有名称值对都放到这个字典中。显然,为了创建键值对,在向形参 **user_info 传递实参时应使用关键字实参。
3.5 函数的存储及编写规范
3.5.1 将函数存储在模块中
使用函数可以将代码块与主程序分离,通过给函数指定描述性的名称,可以让主程序更加容易理解。进一步地,可以将函数存储在被称为模块的独立文件中(扩展名为. py),再将模块导入到主程序中。Python 语言使用 import 保留字实现引用功能。
3.5.2 使用 import
如果要导入整个模块可以使用如下语句:
import <模块名称>
引用整个模块之后,采用 <模块名称>.<函数名称>() 的方式调用具体的函数。
如果需要从模块中导入特定的函数,可以使用下面的语句:
from <模块名称> import <函数名> # 从模块中调用指定函数
from <模块名称> import <函数名 1>, …, <函数名 N> # 从模块调用任意数量的函数
使用这种引用方式,不再需要 <模块名称>. 作为前导,而是使用 <函数名>() 的形式直接调用模块中的函数。
如果要导入的函数名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的函数别名:
from <模块名称> import <函数名> as <函数别名>
使用这种方式后,在程序中使用 <函数别名>() 的形式即可调用函数。
as 除了可以给函数指定别名,还可以给模块指定别名:
import <模块名称> as <模块别名>
在调用时采用 <模块别名>.<函数名>() 的形式调用模块中的函数。这种做法比给函数指定别名会更好,因为模块名通常无关紧要,而函数名则指明了函数的功能。对于理解代码而言,函数名比模块名更重要。
如果要导入模块中的所有函数,使用 * 运算符即可完成该功能:
from <模块名称> import *
此,使用 <函数名>() 的形式即可调用模块中的任意函数。不过,使用并非自己编写的大型模块时,*好不要采用这种导入方法。这是因为如果模块中有函数的名称与当前项目中使用的名称相同,将可能导致错误:Python 可能遇到多个名称相同的函数或变量,进而覆盖函数,而不是分别导入所有函数。
*佳的做法是,要么只导入需要使用的函数,要么导入整个模块并使用句点表示法。
3.5.3 函数的编写规范
编写函数时,应当遵循以下的规范:
为函数指定描述性名称,且只在其中使用小写字母及下划线。
每个函数都应包含阐述功能的简要注释。该注释应紧跟在函数定义后,并采用文档字符串格式。
给形参指定默认值时,等号两边不要有空格。对于函数调用中的关键字实参,也应遵循这种约定。
如果程序或模块包含多个函数,使用两个空行将相邻的函数分开。
所有 import 语句都应放在文件开头,除非文件开头使用了注释来描述整个程序。
如果形参很多,导致函数定义长度超过了 PEP 8 建议的代码行长,可在函数定义中输入左括号后按回车键,并在下一行按两次 Tab 键,从而将形参列表和只缩进一层的函数体区分开。
def <函数名>(
<参数 1>, <参数 2>,
<参数 3>, <参数 4>,
…
):