第8章 函数

8.1 定义函数

8.1.1 向函数传递信息

8.1.2 实参和形参

试一试

8.2 传递实参

鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其中每个实参都由变量名和值组成;还可使用列表和字典

8.2.1 位置实参

8.2.2 关键字实参

关键字实参是传递给函数的名称—值对。你直接在实参中将名称和值关联起来了,因此向函数传递实参时不会混淆。

8.2.3 默认值

编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。因此,给形参指定默认值后,可在函数调用中省略相应的实参。使用默认值可简化函数调用,还可清楚地指出函数的典型用法。

注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的实参。这让Python依然能够正确地解读位置实参。

8.2.4 等效的函数调用

试一试

8.3 返回值

数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。函数返回的值被称为返回值。在函数中,可使用return语句将值返回到调用函数的代码行。返回值让你能够将程序的大部分繁重工作移到函数中去完成,从而简化主程序。

8.3.1 返回简单值

def get_formatted_name(first_name, last_name):
	full_name = first_name + ' ' + last_name
	return full_name.title()

musician = get_formatted_name('jimi', 'hendrix') 
print(musician) 

>>>Jimi Hendrix 

8.3.1 让实参变成可选的

def get_formatted_name(first_name, middle_name, last_name): 
"""返回整洁的姓名""" 
	full_name = first_name + ' ' + middle_name + ' ' + last_name    
	return full_name.title() 

musician = get_formatted_name('john', 'lee', 'hooker') 
print(musician) 

>>>John Lee Hooker 

并非所有的人都有中间名,但如果你调用这个函数时只提供了名和姓,它将不能正确地运行。为让中间名变成可选的,可给实参middle_name指定一个默认值——空字符串,并在用户没有提供中间名时不使用这个实参。为让get_formatted_name()在没有提供中间名时依然可行,可给实参middle_name指定一个默认值——空字符串,并将其移到形参列表的末尾:

def get_formatted_name(first_name, last_name, middle_name=''):
	if middle_name:
		full_name = first_name + ' ' + middle_name + ' ' + last_name  
	else: 
		full_name = first_name + ' ' + last_name 
	return full_name.title() 

musician = get_formatted_name('jimi', 'hendrix') 
print(musician) 
musician = get_formatted_name('john', 'hooker', 'lee')
print(musician) 

>>>Jimi Hendrix 
>>>John Lee Hooker 

8.3.3 返回字典

函数可返回任何类型的值,包括列表和字典等较复杂的数据结构。

8.3.4 结合使用函数和while循环

试一试

8.4 传递列表

你经常会发现,向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容。下面使用函数来提高处理列表的效率。

8.4.1 在函数中修改列表

原程序:

# 首先创建一个列表,其中包含一些要打印的设计 
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = [] 
# 模拟打印每个设计,直到没有未打印的设计为止 
# 打印每个设计后,都将其移到列表completed_models中 
while unprinted_designs: 
	current_design = unprinted_designs.pop() 
	# 模拟根据设计制作3D打印模型的过程 
	print("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() 
	# 模拟根据设计制作3D打印模型的过程  
	print("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 = ['iphone case', 'robot pendant', 'dodecahedron']
completed_models = [] 

print_models(unprinted_designs, completed_models) 
show_completed_models(completed_models) 

相比于没有使用函数的版本,这个程序更容易扩展和维护。如果以后需要打印其他设计,只需再次调用print_models()即可。如果我们发现需要对打印代码进行修改,只需修改这些代码一次,就能影响所有调用该函数的地方;与必须分别修改程序的多个地方相比,这种修改的效率更高。
这个程序还演示了这样一种理念,即每个函数都应只负责一项具体的工作。第一个函数打印每个设计,而第二个显示打印好的模型;这优于使用一个函数来完成两项工作。编写函数时,如果你发现它执行的任务太多,请尝试将这些代码划分到两个函数中。别忘了,总是可以在一个函数中调用另一个函数,这有助于将复杂的任务划分成一系列的步骤。

8.4.2 禁止函数修改列表

有时候,需要禁止函数修改列表。为解决这个问题,可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件。

要将列表的副本传递给函数,可以像下面这样做:

function_name(list_name[:]) 
# 上一个例子
print_models(unprinted_designs[:], completed_models) 

试一试

8.5 传递任意数量的实参

有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参。
例如,来看一个制作比萨的函数,它需要接受很多配料,但你无法预先确定顾客要多少种配料。下面的函数只有一个形参*toppings,但不管调用语句提供了多少实参,这个形参都将它们统统收入囊中:

def make_pizza(*toppings): 
	"""
	打印顾客点的所有配料
	"""    
	print(toppings)
	
make_pizza('pepperoni') 
make_pizza('mushrooms', 'green peppers', 'extra cheese') 

>>>('pepperoni',)  
>>>('mushrooms', 'green peppers', 'extra cheese') 

形参名*toppings中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。函数体内的print语句通过生成输出来证明Python能够处理使用一个值调用函数的情形,也能处理使用三个值来调用函数的情形。它以类似的方式处理不同的调用,注意, Python将实参封装到一个元组中,即便函数只收到一个值也如此。

def make_pizza(*toppings): 
	"""
	概述要制作的比萨
	""" 
	print("\nMaking a pizza with the following toppings:")  
	for topping in toppings: 
		print("- " + topping) 

make_pizza('pepperoni') 
make_pizza('mushrooms', 'green peppers', 'extra cheese') 

>>>Making a pizza with the following toppings:  
>>>- pepperoni
>>>
>>>Making a pizza with the following toppings:  
>>>- mushrooms
>>>- green peppers
>>>- extra cheese

8.5.1 结合使用位置实参和任意数量实参

如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。
例如,如果前面的函数还需要一个表示比萨尺寸的实参,必须将该形参放在形参*toppings的前面:

def make_pizza(size, *toppings):  
	"""概述要制作的比萨""" 
	print("\nMaking a " + str(size) + "-inch pizza with the following toppings:")   
	for topping in toppings: 
		print("- " + 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

8.5.2 使用任意数量的关键字实参

有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键—值对——调用语句提供了多少就接受多少。一个这样的示例是创建用户简介:你知道你将收到有关用户的信息,但不确定会是什么样的信息。在下面的示例中,函数build_profile()接受名和姓,同时还接受任意数量的关键字实参:

def build_profile(first, last, **user_info): 
	"""
	创建一个字典,其中包含我们知道的有关用户的一切
	"""
	profile = {} 
	profile['first_name'] = first 
	profile['last_name'] = last
	for key, value in user_info.items():
		profile[key] = value    
	return profile 

user_profile = build_profile('albert', 'einstein', 
							location='princeton',  
							field='physics') 
print(user_profile) 

>>>{'first_name': 'albert', 'last_name': 'einstein', 'location':'princeton', 'field': 'physics'} 

函数build_profile()的定义要求提供名和姓,同时允许用户根据需要提供任意数量的名称—值对。形参**user_info中的两个星号让Python创建一个名为user_info的空字典,并将收到的所有名称—值对都封装到这个字典中。在这个函数中,可以像访问其他字典那样访问user_info中的名称—值对。

在这里,返回的字典包含用户的名和姓,还有求学的地方和所学专业。调用这个函数时,不管额外提供了多少个键—值对,它都能正确地处理。
编写函数时,你可以以各种方式混合使用位置实参、关键字实参和任意数量的实参。知道这些实参类型大有裨益,因为阅读别人编写的代码时经常会见到它们。要正确地使用这些类型的实参并知道它们的使用时机,需要经过一定的练习。就目前而言,牢记使用最简单的方法来完成任务就好了。你继续往下阅读,就会知道在各种情况下哪种方法的效率是最高的。

试一试

8.6 将函数存储在模块中

函数的优点之一是,使用它们可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。你还可以更进一步,将函数存储在被称为模块的独立文件中,再将模块导入到主程序中。import语句允许在当前运行的程序文件中使用模块中的代码。
通过将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上。这还能让你在众多不同的程序中重用函数。将函数存储在独立文件中后,可与其他程序员共享这些文件而不是整个程序。知道如何导入函数还能让你使用其他程序员编写的函数库。

8.6.1 导入整个模块

要让函数是可导入的,得先创建模块。模块是扩展名为.py的文件,包含要导入到程序中的代码。

8.6.2 导入特定的函数

8.6.3 使用as给函数指定别名

8.6.4 使用as给模块指定别名

8.6.5 导入模块中的所有函数

import语句中的星号让Python将模块pizza中的每个函数都复制到这个程序文件中。由于导入了每个函数,可通过名称来调用每个函数,而无需使用句点表示法。然而,使用并非自己编写的大型模块时,最好不要采用这种导入方法:如果模块中有函数的名称与你的项目中使用的名称相同,可能导致意想不到的结果:Python可能遇到多个名称相同的函数或变量,进而覆盖函数,而不是分别导入所有的函数。
最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰,更容易阅读和理解。

给形参指定默认值时,等号两边不要有空格:

def function_name(parameter_0, parameter_1='default value') 

对于函数调用中的关键字实参,也应遵循这种约定:

function_name(value_0, parameter_1='value') 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值