函数是带名字的代码块,用于完成具体的工作,要执行函数定义的特定任务,可调用该函数。需要在程序中多次执行同一项任务时,无需编写完成该任务的代码,只需要调用执行该任务的函数,让Python运行其中的代码即可。
最后,学习如何将函数存储在成为模块的独立文件中,让主程序文件的组织更有序。
8.1 定义函数
下面是一个打印问候语的简单函数,名为greet_user():
def greet_user():
"""这里是注释"""
print("Hello!")
greet_user()
def告诉Python定义一个函数,这是函数定义。文档字符串(docstring)的注释,描述了函数是做什么的。文档字符串用三引号括起来,Python使用它们来生成有关城西中函数的文档。
8.1.1 向函数传递信息
# def.py
def greet_user(username):
"""显示简单的问候语句"""
print(f"Hello!{username}")
greet_user('guoan')
8.1.2 实参和形参
username是形参
'guoan'是实参
8.2 传递实参
函数定义中也可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多:可以使用位置实参,这要求实参的顺序与形参的顺序相同;也可以使用关键字实参,其中每个实参都由变量名和值组成;还可以使用列表和字典。
8.2.1 位置实参
调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的关联方法是基于实参的顺序。这种关联方式称为位置实参。
# pets2.py
def describe_pet(animal_type,pet_name):
"""显示宠物信息"""
print(f"\n I have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet('hamster','harry')
1.多次调用函数
2.位置实参的顺序很重要
8.2.2 关键字实参
关键字实参是传递给函数的名称对。因为直接在实参中将名称和值关联起来,所以函数传递实参不会混淆。关键字实参让你无需考虑函数调用各种的实参顺序,还清楚的指出了函数调用中各个值的用途。
...
describe_pet(animal_type = 'hamster',pet_name = 'harry')
# 与 describe_pet(pet_name = 'harry',animal_type = 'hamster')等效
使用关键字实参时,无比准确指定函数定义中的形参名。
8.2.3 默认值
编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参默认值后,可在函数调用中省略相应的实参。使用默认值可以简化函数调用,还可以清楚地指出函数的典型用法。
def describe_pet(pet_name,animal_type = 'dog'):
"""显示宠物信息"""
print(f"\n I have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(pet_name = 'willie')
调整函数中形参的位置,是因为,将其设置为默认参数后,Python仍然将此位置的实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个参数将关联到函数定义中的第一个形参。
注意,使用默认值时,必须先在形参列表中列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确的解读位置实参。
8.2.4 等效的函数调用
三种方式皆可。。
8.2.5 避免实参错误
例如没有输入实参
8.3 返回值
函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值。函数返回的值称为返回值。在函数中,可以使用return语句将值返回到调用函数的代码行。返回值让你能够将程序中的大部分繁重工作移到函数中完成,从而简化主程序。
8.3.1 返回简单值
# formatted_name.py
def get_formatted_name(first_name,last_name):
"""返回整洁的姓名"""
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('Guo','an')
print(musician)
8.3.2 让实参变成可选的
有时候,需要让实参变成可选的,这样使用函数的人就只能再必要时提供额外的信息。可使用默认值来让实参变成可选的。
# formatted_name.py
def get_formatted_name(first_name,middle_name,last_name):
"""返回整洁的姓名"""
full_name = f"{first_name} {middle_name} {last_name}"
return full_name.title()
musician = get_formatted_name('Guo','xiao','an')
print(musician)
但是实际情况是,并非所有的人都有中间名,但如果调用这个函数时,只提供了名和姓,它将不能正常运行。为了让中间名变成可选的,可以给middle_name指定一个空的默认值,并在用户没有提到中间名时不适用这个形参。为让get_formatted_name()在没有提供中间名时依然可行,可将形参middle_name的默认设置为空字符串,并将其移到形参列表的末尾:
# formatted_name.py
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('Guo','an',middle_name = 'xiao')
computer = get_formatted_name('Guo','an')
print(musician)
print(computer)
可选值让函数能够处理各种不同的情形,同时确保函数调用尽可能简单。
8.3.3 返回字典
函数可返回任何类型的值,包括列表和字典等较为复杂的数据结构。下面的函数接受姓名的组成部分,并返回一个表示人名的字典:
def build_person(first_name,last_name):
"""返回一个字典,其中包含有关一个人的信息"""
person = {'first':first_name,'last':last_name}
return person
musician = build_person('Guo','an')
print(musician)
函数build_person()接受名和姓,并将这些,并将这些值放到字典中。存储first_name的值时,使用的键为'last'。最后,返回表示人的整个字典。
这个函数接受简单文本信息,并将其放在一个更合适的数结构中,不仅可以打印,还能以其他方式处理它们。
当前,字符串'Guo'和'an'被标记为名和姓。可以轻松地扩展这个函数,使其接受可选值,如中间值、年龄、职业或其他任何要存储的信息。
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('Guo','an',age=27)
print(musician)
在函数定义中,新增加了一个可选形参age,并将其默认值设置为特殊值None(表示变量没有值)。可将None视为占位值。在条件测试中,None相当于False。如果函数调用中包含形参age的值,这个值将被存储到字典中。在任何情况下,这个函数都会存储人的姓名,但可进行修改,使其同时存储有关人的其他信息。
8.3.4 结合使用函数和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:")
f_name = input("First name: ")
l_name = input("Last name: ")
formatted_name = get_formatted_name(f_name,l_name)
print(f"\bHello,{formatted_name}")
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("\nEnter 'q' 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"\bHello,{formatted_name}")
8.4 传递列表
向函数传递列表很有用,其中包含的可能是名字、数或更复杂的对象(如字典)。
将列表传递给函数后,函数就能直接访问其内容。下面使用函数来提高处理列表的效率。
假设有一个用户列表,我们要问候其中的每位用户。下面的示例将包含名字的列表传递给一个名为greet_users()的函数,这个函数问候列表中的每个人:
def greet_users(names):
"""向列表中的每位用户发出简单的问候。"""
for name in names:
msg = f"Hello!,{name.title()}"
print(msg)
usernames = ['Guoan','xiaoan','knight7z']
greet_users(usernames)
8.4.1 在函数中修改列表
在函数中对列表的修改是永久性的。
# 首先创建一个列表,其中包含一些要打印的设计
upprinted_designs = ['phone case','robot pendant','dodecahedron']
completed_models = []
# 模拟打印每个设计,直到没有未打印的设计为止
# 打印每个设计后,都将其移到列表completed_models
while upprinted_designs:
current_design = upprinted_designs.pop()
print(f"Printing model:{current_design}")
completed_models.append(current_design)
# 显示打印好的所有模型
print("\nThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
为重新组织这些代码,可编写两个函数,每个都做一件具体的工作。
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("\bThe following models have been printed:")
for completed_model in completed_models:
print(completed_model)
upprinted_designs = ['phone case','robot pendant','dodecahedron']
completed_models = []
print_models(upprinted_designs,completed_models)
show_completed_models(completed_models)
每个函数都应只负责一项具体的工作。第一个函数打印每个设计,第二个显示打印好的模型。这优于使用一个函数来完成来这亮项工作。编写函数时,如果发现它执行的任务太多,请尝试将这些代码划分到两个函数中。总是可以在一个函数中调用另外一个函数,这有助于将复杂的任务划分成一系列步骤。
8.4.2 禁止函数修改列表
为了给函数传递副本而非本体。
function_name(list_name[:])
切片表示法[:]创建列表的副本。在上一个代码中,若是不想清空未打印的设计列表,可像下面这样调用print_models():
print_models(upprinted_designs[:],completed_models)
处理大表格,若非必要,还是直接传递列表较好。
8.5 传递任意数量的实参
有时候预先不知道需要接收多少个参数,Python允许函数从调用语句中收集任意数量的实参。
例如:
def make_pizza(*toppings):
"""打印顾客点的所有配件"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms','green peppers','extra cheese')
形参中的*让Python创建一个名为toppings的空元组,并将收到的所有制都封装到该元组中。
def make_pizza(*toppings):
"""概述要做的比萨"""
print("\nMaking a pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms','green peppers','extra cheese')
不管函数收到的实参是多少个,这个方法都管用。
8.5.1 结合使用位置实参和任意数量实参
如果要让函数接受不同类型的实参,必须在函数定义中接纳任意数量实参的形参放在最后。
Python将先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
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')
提示:通用形参名*args,它也收集任意数量的位置实参
8.5.2 使用任意数量的关键字实参
有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对————调用语句提供了多少就接受多少。
一个示例是创建用户简洁:你知道将接受有关用户的信息,但不确定会是什么样的信息。在下面的示例中,函数build_profile()接受名和姓,还接受任意数量的关键字实参:
def build_profile(first,last,**user_info):
"""创建一个字典,其中包含我们直到的有关用户的一切。"""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('Guo','an',favorit = 'c',love = 'blue')
print(user_profile)
形参**user_info中的两个*让Python创建一个名为user_info的空字典,并将收到的所有名称都放入这个字典中。
调用这个函数时,不管提供了多少额外键值对,他都可以正确处理。
提示:会经常看到形参名字**kwargs,它用于收集任意数量的关键字实参。
8.6 将函数存储在模块中
使用函数的有点之一是可以将代码于主程分离。通过给函数指定描述性名称,可让主程序容易理解得多。还可以更进一步,将函数存储在称为模块的独立文件中,再将模块导入到主程序中。
import 语句允许在当前允许的程序文件中使用模块中的代码,
通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上。还能让在众多不同的程序中重用函数。将函数存储在独立文件中后,可与其他程序员共享这些文件而不是整个程序
8.6.1 导入整个模块
要让函数是可导入的,首先创建模块。模块的扩展名为.py的文件。包含要导入到程序中的代码。
def make_pizza(size,*toppings):
"""概述要做的比萨"""
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
Python读取这个文件时,代码行import pizza让Python打开文件pizza.py并将其中的所有函数复制到这个文件中。
这就是一种导入方法:只需编写一个import语句并在其中指定模块名,就可以在程序中使用该模块中的所有函数。如果使用这种import语句导入了名为model_name.py的模块,就可以使用下面的语法来使用其中任何一个函数:
model_name.function_name()
8.6.2 导入特定的函数
还可以导入模块中的特定函数,这种导入方法的语句:
from module_name import function_name
# 通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:
from module_name import function_0,function_1,function_2
# 对于前面示例:
from pizza import make_pizza
make_pizza(...)
使用这种语法时,调用函数无需使用句点。由于在import语句中显式的导入了函数make_pizza(),调用时只需指定其名称即可。
8.6.3 使用as给函数指定别名
若与当前函数名冲突或者太长,可以起一个外号
form pizza import make_pizza as mp
mp(...)
通用:
form model_name import function_name as fn
8.6.4 使用as给模块指定别名
还可以给模块指定别名。
import pizza as p
p.make_pizza(...)
通用:
import module_name as mn
8.6.5 导入模块中的所有函数
使用(*)可让Python导入模块中的所有函数:
form pizza import *
make_pizza(...)
import语句中的星号让Python将模块pizza中的每个函数都复制到这个程序文件中。由于导入了每个函,可通过名称来调用每个函数,而无需使用句点表示法。
使用并非自己编写的大型模块时候,不要采用这种方法,这是因为如果模块中有函数名称相同的函数时候,可能导致错误。
要么只导入使用的代码,要么导入整个模块使用句点表示。
form module_name import *
8.7 函数编写指南
- 给函数指定描述性名称
- 阐述功能
- 给形参指定默认值的时候,=号两边不要有空格。
def function_name(parameter_0,parameter_1='default value')
对于调用函数中的关键字实参,也遵循这种约定:
function_name(value_0,parameter_1='value')
- 过长缩进(79字符)
- 程序包含多个函数,函数与函数间两个空行隔开
- 除了开头注释外,其余所有import放在文件开头
8.8 小结
学习编写函数,传递参数等。
叨叨几句... NOTHING