1.定义函数
下面是一个打印问候语的简单函数,名为greet_user():
def greet_user():
"""显示简单的问候语"""
print("Hello!")
greet_user()
其中,def关键字告诉Python你要定义一个函数,然后提供了名为greet_user的函数名,在其后的圆括号内指出函数要完成这项功能,需要什么信息(参数)。冒号后缩进的代码段被称为函数体,第二行的文本称为“文档字符串”的注释,描述函数是做什么的,用三引号括起。第三行处的代码为函数要执行的代码。
1.1向函数传递信息
可以通过在函数名圆括号里添加信息,来向函数传递信息。
def greet_user(username):
"""显示简单的问候语"""
print(f"Hello,{username.title()}")
greet_user('john')
1.2实参和形参
通常情况下,我们把在函数定义中在函数名后括号里添加的变量称为形参,把实际调用时在函数名后括号里添加的变量叫实参。
2.传递实参
函数定义是可能包含多个形参,因此函数调用时也可能包含多个实参。向函数传递实参的方式有很多,可使用位置参数,这要求实参顺序与形参相同,也可以使用关键字实参,,其中每个实参都是有变量名和其值组成。还可以使用列表和字典。
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('dog','willing')
注意:(1)实参的顺序与形参顺序相同,这很重要。(2)可以根据需要调用任意次函数。
2.2关键字实参
关键字实参是传递给函数名称值对,因为直接在实参中将名称和值关联起来,所以向函数传递实参时不会混淆。关键字实参让你无需考虑函数调用中实参的顺序,还清楚的指出了各个值的用途。
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(pet_name = 'harry',animal_type = 'hamster')
注意:使用关键字实参时,务必准确指定函数定义中的形参名。
2.3默认值
编写函数时,可以给每个形参指定默认值。这样在调用函数时指定了实参,函数会使用指定
的实参,否则,使用形参的默认值。因此,给形参指定默认值后,可在函数调用时省略相应的实参。
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('willing')
describe_pet(pet_name='willing')
注意,这里修改了函数的定义,因为给animal_type指定了'dog'的默认值,所以无需通过实参来指定动物的类型,所以在函数调用时可以只包含一个实参,即动物的名字。但是,Python依然将这个实参看作是位置实参,所以要改变函数定义时形参的位置。
综上,在使用默认值时,必须先列出未定义默认值的实参,再列出已定义默认值的实参。
2.4等效的函数调用
鉴于可以混合使用位置实参、默认值、关键字实参,通常有多种等效的函数调用。
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('willing')
describe_pet(pet_name='willing')
describe_pet('willing','dog')
describe_pet('harry','hamster')
describe_pet(pet_name='harry',animal_type='hamster')
使用那种调用方式并不重要,主要是能达到你的预期,方便你的理解。
2.5避免实参错误
在调用函数时,会出现实参不匹配错误,这是要注意函数定义中的形参顺序要与函数定义中的实参顺序一致。
3.返回值
函数并非总是直接显示输出的,还可以处理一些数据,并返回一个或一组值。函数返回的值称为返回值。在函数中可以使用return语句将值返回到调用函数的代码行。
3.1返回简单值
返回函数调用值的时候,注意要设置一个变量,以便将函数返回值赋给它。
def get_formatted_name(first_name,last_name):
"""返回整洁的名字"""
full_name=f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi','hendrix')
print(musician)
3.2让实参变成可选的
有时候,要将实参变成可选的,这样使用函数的人只要在必要的时候提供额外的信息即可。可以使用默认值来让实参变成可选的。
例如,假设要扩展函数get_formatted_name(),使其能处理中间名。可以作如下修改:
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('john','lee','hooker')
print(musician)
但是,并非所有的人都有中间名,所以要让中间名变成可选的,可以给中间名设定一个默认值,即空集,还要注意一旦使用了默认值,要将未定义默认值的形参放在前面,定义过默认值的形参放在后面,代码如下:
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('jimi','hendrix')
print(musician)
musician = get_formatted_name('john','lee','hooker')
print(musician)
3.3返回字典
函数可以返回任何数据结构,包括字典(一系列键值对)、列表等复杂的数据结构。
def build_persion(first_name, last_name):
"""返回一个字典,其中包含一个人的信息"""
persion = {'first': first_name, 'last': last_name}
return persion
musician = build_persion('jimi','hendrix')
print(musician)
让函数接收简单的文本信息,并将其放在一个更合适的数据结构中,会让你更好的处理他们。如下,我们可以在上面代码段的基础上,添加年龄。
def build_person(first_name, last_name, age = None):
"""返回一个字典,其中包含一个人的信息"""
persion = {'first': first_name, 'last': last_name}
if age:
persion['age'] = age
return persion
musician = build_person('jimi', 'hendrix')
print(musician)
musician = build_person('jimi', 'hendrix', 27)
print(musician)
我们在上述代码段中加入了age,但是,并不是所有的人都要加入age选项,所以要使用默认值将其变为可选的,一旦使用了默认值,就必须把未用默认值的形参放在函数定义中的前面,已定义默认值的实参放在函数定义的后面。
3.4结合使用函数和while循环
可将函数与本书介绍过的任何Python结构结合起来使用,如下:
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 tine 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}!")
4.传递列表
将列表传递给函数,函数就可以直接访问列表。如下:
def greet_users(names):
"""向列表中的每位用户发出简单的问候"""
for name in names:
msg = f"Hello,{name.title()}!"
print(msg)
usernames = ['hannah','ty','margot']
greet_users(usernames)
4.1在函数中修改列表
将列表传递给函数后,函数就可以对其进行修改。在函数中对列表进行的修改都是永久性的,这让你能高效的处理列表中的数据。
def print_models(unprinted_design, completed_design):
"""模拟打印每个设计,直到未打印的设计都打印完毕"""
while unprinted_design:
current_design = unprinted_design.pop()
print(f"Printing model: {current_design}")
completed_design.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', 'dade']
completed_models=[]
print_models(unprinted_designs, completed_models)
show_completed_models(completed_models)
上段代码中,定义了两个函数来实现这一功能。注意,我们在定义一个函数时,如果这一函数要实现的功能太多,要们要将它拆分成多个函数,分别实现一个功能,这样易于理解与修改。在定义第一个函数时,使用了.pop()弹出末尾元素,以及.append()将元素添加到列表末尾这两条语句,第二个函数使用了for循环。然后,在实现相应功能时,分别调用相应的函数即可。
4.2禁止函数修改列表
有时候,需要禁止函数修改列表。这样我们要向函数提供一个原函数的副本,即:切片就可以实现该功能。如下:
print_models(unprinted_designs[:], completed_models)
切片表示法([:])可以创建列表副本,这样传递给函数的就是列表的副本,而不是列表原件。但是,要注意这样做的话会花费更多的时间和内存。
5.传递任意数量的实参
有时候,不知道函数预先要接受多少个实参,好在Python允许函数从调用语句中接受任意数量的实参。
def make_pizza(*toppings):
"""概述要制作的比萨"""
for topping in toppings:
print(f"- {topping}")
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
*toppings创建了一个空元组,并将收到的值都封装到这个元组中,这样函数就可以从这个元组中调用提供的任意数量的实参。
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(31, 'mushrooms', 'green peppers', 'extra cheese')
基于上述函数的定义,Python将第一个实参赋值给size,将其后所有的实参赋给*toppings。要注意,你经常会看到通用形参名*args,它也是收集任意数量的位置实参。
5.2使用任意数量的关键字实参
有时候,要接受任意数量的实参,但预先不知道传递给函数的时什么样的信息,在这种情况下可以将函数编写成可以接受任意数量的键值对——调用语句提供了多少就接受多少。
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)
形参**user_info的两个星号是让Python创建名为user_info的空字典并将所有接收到的名称值对都放到这个字典中。这样,函数可以向访问其它字典那样访问user_info中的名称值对。其中,定义location、field时使用了关键字实参。注意,一是要把定义的任意数量的关键字实参放在最后,二是你经常会看到形参名**kwargs,它用于收集任意数量的关键字实参。
6.将函数储存在模块中
使用函数的优点之一就是将代码块与主程序分离,还可以更进一步,将函数储存在称为模块的独立文件中,再将模块导入主程序中。import语句允许在当前运行的程序文件中使用模块中的代码。
6.1导入整个模块
要让函数导入模块,首先得出创建模块。模块是名为.py的文件,包含要导入到程序中的代码。
import pizza
make_pizza(16, 'pepperoni')
make_pizza(31, 'mushrooms', 'green peppers', 'extra cheese')
pizza是模块所在的文件名,导入后,就可以直接调用函数。
6.2导入特定函数
from module_name import function_name
from module_name import function_0, function_1, function_2
可以通过上述第一种方法,导入一个特定函数吗,也可以使用第二种方法,导入多个特定函数。
6.3使用as给函数指定别名
还可以给函数指定别名。通过给函数指定一个简短的别名,让你能够更轻松地调用模块中的函数。
import module_name as mn
import pizza as p
p.make_pizza(16,'pepperoni')
6.4导入模块中的所有函数
使用星号(*)运算符可以让Python导入模块中的所有函数。
from pizza import *
make_pizza(16, 'pepperoni')
make_pizza(31, 'mushrooms', 'green peppers', 'extra cheese')
from module_name import *
7.函数编写指南
编写函数时,应给函数指定描述性名称,并采用文档字符串格式简要介绍其功能,还要注意,一是,给形参指定默认值时,等号两边不要有空格,调用关键字实参时,也不要有空格。二是,如果程序包含多个模块和函数应该使用两个空行将其分开。三是,import语句都应该放在开头。