目录
8.1 定义函数
下面是一个打印问候语的简单函数,名为
greet_user()
:
def greet_user():
"""显示简单的问候语。"""
print("Hello!")
greet_user()
本例演示了最简单的函数结构。
代码行使用关键字
def 来告诉Python,你要定义一个函数。这是
函数定义 ,向Python指出了函数名,还可能在圆括号内指出函数为完成任务需要什么样的信息。在这里,函数名为greet_user() ,它不需要任何信息就能完成工作,因此括号是空的(即便如此,括号也必不可少)。最后,定义以冒号结尾。
紧跟在
def greet_user():
后面的所有缩进行构成了函数体。第二行的文本是称为文档字符串 (docstring)的注释,描述了函数是做什么的。文档字符串用三引号括起,Python使用它们来生成有关程序中函数的文档。
代码行
print("Hello!")
是函数体内的唯一一行代码,因此greet_user()
只做一项工作:打印
Hello!
。
要使用这个函数,可调用它。
函数调用
让Python执行函数的代码。要
调用函数,可依次指定函数名以及用圆括号括起的必要信息,如
所示。由于这个函数不需要任何信息,调用它时只需输入
greet_user()
即可。和预期一样,它打印Hello!:
Hello!
8.1.1 向函数传递信息
只需稍作修改,就可让函数
greet_user()
不仅向用户显示
Hello! ,还将用户的名字作为抬头。为此,可在函数定义
def greet_user() 的括号内添加username
。通过在这里添加
username
,可让函数接受你给
username 指定的任何值。现在,这个函数要求你调用它时给
username 指定一个值。调用greet_user()
时,可将一个名字传递给它,如下所示:
def greet_user(username):
"""显示简单的问候语。"""
print(f"Hello, {username.title()}!")
greet_user('jesse')
代码
greet_user('jesse')
调用函数
greet_user() ,并向它提供执行函数调用
print()
所需的信息。这个函数接受你传递给它的名字,并向这个人发出问候:
Hello, Jesse!
同样,
greet_user('sarah')
调用函数
greet_user()
并向它传递'sarah',从而打印
Hello, Sarah!
。可根据需要调用函数
greet_user() 任意次,调用时无论传入什么名字,都将生成相应的输出。
8.1.2 实参和形参
前面定义函数
greet_user()
时,要求给变量
username 指定一个值。调用这个函数并提供这种信息(人名)时,它将打印相应的问候语。
在函数
greet_user()
的定义中,变量
username
是一个
形参 (parameter),即函数完成工作所需的信息。在代码
greet_user('jesse')
中,值
'jesse' 是一个
实参 (argument),即调用函数时传递给函数的信息。调用函数时,将要让函数使用的信息放在圆括号内。在
greet_user('jesse')
中,将实参
'jesse' 传递给了函数
greet_user()
,这个值被赋给了形参
username
。
注意 大家有时候会形参、实参不分,因此如果你看到有人将函数定义中的变量称为实参或将函数调用中的变量称为形参,不要大惊小怪。
8.1.3 练习
练习8-1:消息
编写一个名为
display_message() 的函数,它打印一个句子,指出你在本章学的是什么。调用这个函数,确认显示的消息正确无误。
def display_message():
"""something I learned in this chapter"""
print("使用def定义一个函数")
display_message()
练习8-2:喜欢的图书
编写一个名为
favorite_book() 的函数,其中包含 一个名为
title 的形参。这个函数打印一条消息,下面是一个例子。
One of my favorite books is Alice in Wonderland.
调用这个函数,并将一本图书的名称作为实参传递给它。
def favorite_book(title):
"""最喜欢的书的名字"""
print(f"One of my favorite books is {title}")
title = input("please input what your favorite book:\n")
favorite_book(title)
8.2 传递实参
函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多:可使用
位置实参 ,这要求实参的顺序与形参的顺序相同;也可使用
关键字实参 ,其中每个实参都由变量名和值组成;还可使用列表和字典。下面依次介绍这些方式。
8.2.1 位置实参
调用函数时,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('hamster', 'harry')
这个函数的定义表明,它需要一种动物类型和一个名字。调用describe_pet() 时,需要按顺序提供一种动物类型和一个名字。例如,在刚才的函数调用中,实参
'hamster'
被赋给形参
animal_type
,而实参
'harry' 被赋给形参
pet_name 。在函数体内,使用了这两个形参来显示宠物的信息。
输出描述了一只名为Harry的仓鼠:
I have a hamster.
My hamster's name is Harry.
a. 多次调用函数
可以根据需要调用函数任意次。要再描述一个宠物,只需再次调用describe_pet()
即可:
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('hamster', 'harry')
describe_pet('dog', 'willie')
第二次调用
describe_pet()
函数时,向它传递了实参
'dog'
和'willie'。与第一次调用时一样,Python将实参
'dog'
关联到形参
animal_type ,并将实参
'willie'
关联到形参pet_name 。与前面一样,这个函数完成了任务,但打印的是一条名为Willie的小狗的信息。至此,有一只名为Harry的仓鼠,还有一条名为Willie的小狗。
多次调用函数是一种效率极高的工作方式。只需在函数中编写一次描述宠物的代码,然后每当需要描述新宠物时,都调用该函数并向它提供新宠物的信息。即便描述宠物的代码增加到了10行,依然只需使用一行调用函数的代码,就可描述一个新宠物。
在函数中,可根据需要使用任意数量的位置实参,Python将按顺序将函数调用中的实参关联到函数定义中相应的形参。
b. 位置实参的顺序很重要
使用位置实参来调用函数时,如果实参的顺序不正确,结果可能出乎意料:
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('harry', 'hamster')
在这个函数调用中,先指定名字,再指定动物类型。由于实参
'harry' 在前,这个值将赋给形参
animal_type
。同理,
'hamster'
将赋给形参pet_name。结果是有一个名为Hamster的harry:
I have a harry.
My harry's name is Hamster.
如果你得到的结果像上面一样可笑,请确认函数调用中实参的顺序与函数定义中形参的顺序一致。
8.2.2 关键字实参
关键字实参 是传递给函数的名称值对。因为直接在实参中将名称和值关联起来,所以向函数传递实参时不会混淆(不会得到名为Hamster的harry这样的结果)。关键字实参让你无须考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。
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='hamster', pet_name='harry')
函数
describe_pet() 还和之前一样,但调用这个函数时,向Python明确地指出了各个实参对应的形参。看到这个函数调用时,Python知道应该将实参'hamster'和
'harry'
分别赋给形参
animal_type
和
pet_name 。输出正确无误,指出有一只名为Harry的仓鼠。
关键字实参的顺序无关紧要,因为Python知道各个值该赋给哪个形参。下面两个函数调用是等效的:
describe_pet(animal_type='hamster', pet_name='harry')
describe_pet(pet_name='harry', animal_type='hamster')
注意 使用关键字实参时,务必准确指定函数定义中的形参名。
8.2.3 默认值
编写函数时,可给每个形参指定默认值 。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。
例如,如果你发现调用
describe_pet() 时,描述的大多是小狗,就可将形参animal_type
的默认值设置为
'dog'
。这样,调用
describe_pet() 来描述小狗时,就可不提供这种信息(关键字方式):
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')
这里修改了函数
describe_pet()
的定义,在其中给形参
animal_type 指定了默认值
'dog'
。这样,调用这个函数时,如果没有给
animal_type 指定值,Python就将把这个形参设置为
'dog'
:
I have a dog.
My dog's name is Willie.
请注意,在这个函数的定义中,修改了形参的排列顺序。因为给animal_type 指定了默认值,无须通过实参来指定动物类型,所以在函数调用中只包含一个实参——宠物的名字。然而,Python依然将这个实参视为位置实参,因此如果函数调用中只包含宠物的名字,这个实参将关联到函数定义中的第一个形参。这就是需要将pet_name
放在形参列表开头的原因。
现在,使用这个函数的最简单方式是在函数调用中只提供小狗的名字(位置方式):
describe_pet('willie')
这个函数调用的输出与前一个示例相同。只提供了一个实参
'willie' ,这个实参将关联到函数定义中的第一个形参
pet_name
。由于没有给
animal_type 提供实参,Python将使用默认值
'dog'
。
如果要描述的动物不是小狗,可使用类似于下面的函数调用:
describe_pet(pet_name='harry', animal_type='hamster')
由于显式地给
animal_type
提供了实参,Python将忽略这个形参的默认值。
注意 使用默认值时,必须先在形参列表中列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确地解读位置实参。