Python 基础 -- 函数
- 函数是带名字的代码块;
- 函数是组织好的,可以重复使用的,用来实现单一或相关功能的代码段;
- Python 提供了许多内建函数,比如
print()
;- 自己编写的函数称为自定义函数;
1. 定义函数
-
首先以一个简单的函数为例,来讲解函数:
def greet_user(): """显示简单的问候语""" print("Hello!") greet_user()
- 第一行中:
def
是定义函数的关键字,此关键字将会告诉 Python 这段程序是函数;greet_user()
是函数名称,括号中,可以传递参数给函数,通过函数处理在输出,也可以不传递,就像这个函数,只是简单的打印一句话;:
是必循存在的,就跟之前的if
语句以及for
循环一样,用来表示下面紧跟着的缩进内容都是这个函数的一部分;
- 第二行:
- 六个双引号之间的文本被称为文档字符串,算是一种注释,对函数的描述;
- 第三行:
- 是函数
greet_user()
唯一的一行代码,只做打印输出;
- 是函数
- 第一行中:
-
输出结果:
1.1 函数定义规则及语法
-
定义函数规则:由上述例子,我们可以简单归纳以下定义函数的规则;
- 函数代码块以
def
关键字开头,后接函数标识符名称和圆括号,最后以冒号结尾; - 函数代码块中的内容缩进;
- 函数第一行语句可以选择性的使用文档字符串进行注释说明;
- 函数代码块以
-
定义函数语法:
def function_name(): """文档字符串""" function_suite
1.2 向函数传递信息
-
对函数进行修改,可以使得函数
greet_user()
不仅可以向用户显示 Hello,还可以将用户的名字添加进入; -
为此,只需要在括号中添加适当的参数即可;
-
这里通过
username
,就可以让函数接受你给username
的任何值; -
当添加了参数
username
后,在调用函数时,就必须要给username
指定一个值;def greet_user(username): """显示简单的问候语""" msg = "Hello, " + username + "!" print(msg) greet_user('Eric')
-
当调用代码时,传递进入
Eric
之后,函数会接受传递的名字,并向这个用户发出问候:
1.3 形参和实参
- 形参:就是函数完成工作时,所需要的信息,例如 1.2 节中的
username
,当函数在没有被调用时,需要逻辑上的完整,就需要用这种形式上的参数,来完成工作; - 实参:调用函数时传递给函数的信息,例如 1.2 节中调用函数时,传递的
Eric
; - 在 1.2 节中,我们使用函数
greet_user()
时,将实参Eric
传递给了函数greet_user()
,然后将实参的值,存储在了形参username
中;
1.4 练习
-
编写一个名为
display_messge()
的函数,它打印一个句子,指出本章学的内容,并调用这个函数,确认显示消息正确无误:
-
编写一个名为
favorite_book()
的函数,其中包含一个名为title
的形参。这个函数打印一条消息,如 One of my favorite books is Alice in Woderland。调用这个函数,并将一本图书的名称作为实参传递给它。
2. 传递实参
- 函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参;
- 向函数传递实参的方式有很多种:
- 位置实参:要求实参的顺序与形参的顺序相同;
- 关键字实参:其中每个实参都由变量名和值组成;
- 还可以使用列表和字典;
2.1 位置实参
-
调用函数时, Python 必须将函数调用中的每个实参都关联到函数定义中的一个形参。
-
所以,最简单的关联方式就是基于实参的顺序,这种关联方式称为位置实参;
-
以一个简单的显示从无信息的函数为例:
def describe_pet(animal_type, pet_name): """显示宠物信息""" msg = "\nI have a " + animal_type + "." print(msg) msg = "My " + animal_type + "'s name is " + pet_name.title() + "." print(msg) describe_pet('hamster', 'harry')
- 第一行中:有两个形参
animal_type
和pet_name
,当调用函数时,会将传入的实参值进行存储,并等待函数工作时的调用;
- 第一行中:有两个形参
-
结果:
-
函数可以根据需求调用多次;
-
如果再要描述一个宠物,只需再次调用
describe_pet()
即可:
-
调用函数多次是一种效率极高的工作方式;
-
只需要在函数中编写描述宠物的代码一次,然后每当需要描述新宠物时,都可以调用这个函数,并向他提供新宠物的信息;
-
位置实参的顺序很重要,如果传入的实参顺序不对,将会出乎意料的结果:
2.2 关键字实参
-
关键字实参是传递给函数的名称-值对;
-
直接在实参中将名称和值关联起来,因此向函数传递实参时不会混淆;
-
关键字实参无需考虑函数调用中的实参顺序;
-
关键字实参清楚的指出了函数调用中的各个值的用途;
-
使用关键字实参,必须准确的指定函数定义中的形参名;
2.3 默认值
-
有时候,编写一类动物程序时,只是名字不同,种类都相同的情况下,可以个动物类型设定默认值,可以减少参数的传递;
-
一般在提供了实参的情况下,函数会使用指定的实参;
-
如果没有指定,将使用形参的默认值;
-
在有指定默认值的情况下,需要将没有默认值的形参放在形参列表中的前边,将有默认值的放在最后;
-
上述暂时理解为规定,目前没接触到为什么;
2.4 练习
-
编写一个名为
make_shirt()
的函数,它接受一个尺码以及要印到 T 恤上的字样,这个函数打印一个句子,概要说明 T 恤的尺码和字样;
-
编写一个名为
describe_city()
的函数,它接受一座城市的名字以及该城市所属的国家。这个函数将打印一个简单的句子,如 Reykjavik is in Iceland。
3. 返回值
- 函数并非总是直接显示输出,相反,它可以处理一些数据,并返回一个或一组值。
- 函数返回的值被称为返回值;
- 在函数中,使用
return
语句将返回调用函数的代码行; - 返回值可以将程序中大部分的工作移到函数中去完成,从而简化主程序;
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)
-
结果:
3.2 将实参变成可选项
-
有的人会有中间名,但是有的人没有,但为了简便起见,不可能写上两个程序,这样做会使得调用函数容易出错;
-
我们先将中间名默认设置为空字符,然后在函数体中根据判断是否需要使用中间名;
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('jimi', 'hendrix', 'lee') print(musician)
- 在函数体中,使用
if
进行判断时,只要字符串非空,则 Python 将会解读为True
,因此,如果提供中间名,将会执行if
语句;
- 在函数体中,使用
-
结果显示:
3.3 返回字典
-
函数可以返回任何类型的值,包括列表和字典等较复杂的数据结构;
-
下面的函数接受姓和名返回一个表示人的字典:
def build_person(first_name, last_name): """返回一个字典,包含有关一个人的信息""" person = {'first': first_name, 'last': last_name} return person musician = build_person('jimi', 'hendrix') print(musician)
-
结果:
3.4 结合使用函数和 while 循环
-
将之前的
get_formatted_name()
函数和while
循环结合使用,以更正规的方式问候用户;def get_formatted_name(first_name, last_name): """返回整洁的姓名""" full_name = first_name + ' ' + last_name return full_name.title() while True: msg = "\nPlease tell me your name: " print(msg) msg = "(enter 'q' at any time to quit)" print(msg) 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) msg = "\nHello, " + formatted_name + "!" print(msg)
- 此程序使用提示语句,当用户不需要输入时,将使用
break
退出循环语句;
- 此程序使用提示语句,当用户不需要输入时,将使用
-
结果演示:
3.5 练习
-
编写一个名为
city_country()
的函数,它接受城市的名称及其所属的国家;
-
编写一个名为
make_album()
的函数,它创建一个描述音乐专辑的字典;# 定义函数 def make_album(singer, album, number = ''): if number: int_number = int(number) message_1 = {'singer' : singer.title(), 'Ablum' : album.title(), 'Number' : int_number} else: message_1 = {'singer' : singer.title(), 'Ablum' : album.title()} return message_1 # 位置实参 singer_album = make_album('singer_A', 'Album_A') print(singer_album) singer_album = make_album('singer_B', 'Album_B', '9') print(singer_album) # 关键词实参 singer_album = make_album(singer = 'singer_C', album = 'Album_C') print(singer_album) singer_album = make_album(singer = 'singer_D', album = 'Album_D', number = '4') print(singer_album) # 混合实参 singer_album = make_album('singer_E', album = 'Album_E') print(singer_album) singer_album = make_album('singer_F', album = 'Album_F', number = '15') print(singer_album)
-
结果:
{'singer': 'Singer_A', 'Ablum': 'Album_A'} {'singer': 'Singer_B', 'Ablum': 'Album_B', 'Number': 9} {'singer': 'Singer_C', 'Ablum': 'Album_C'} {'singer': 'Singer_D', 'Ablum': 'Album_D', 'Number': 4} {'singer': 'Singer_E', 'Ablum': 'Album_E'} {'singer': 'Singer_F', 'Ablum': 'Album_F', 'Number': 15}
-
-
结合上述的
make_album()
函数和while
循环,打印字典;# 定义函数。输入 歌手名字 歌手专辑 专辑包含的歌曲数 def make_album(singer, album, number=''): '''以字典的方式输出''' message_1 = {'singer' : singer.title(), 'Ablum' : album.title()} if number: int_number = int(number) message_1['number'] = int_number return message_1 # while 循环,录入并输出歌手信息 while True: print("\nPlease enter the singer's information: ") print("(enter 'q' at any time to quit!)") singer_name = input('singer_name: ') if singer_name == 'q': break singer_album = input('singer_album: ') if singer_album == 'q': break '''询问是否输入歌手专辑的歌曲数''' print("Is it convenient to enter the number of songs contained in the album?(yes/no)") number = input() if number == 'yes': album_number = input('number_of_songs: ') if album_number == 'q': break else: album_number = int(album_number) singer_information = make_album(singer='singer_name', album='singer_album', number=album_number) print("\n") print(singer_information) elif number == 'no': singer_information = make_album(singer='singer_name', album='singer_album') print("\n") print(singer_information)
- 结果:
- 结果:
4. 传递列表
-
向函数传递的列表可能是名字、数字或者更复杂的对象,例如字典;
-
将列表传递给函数后,函数就可以直接访问其内容;
-
假设有一个用户列表,我们需要问候每位用户;
def greet_users(names): """向列表中的每位用火狐都发出简单的问候""" for name in names: msg = "Hello, " + name.title() + "!" print(msg) usernames = ['hannah', 'ty', 'margot'] greet_users(usernames)
-
结果显示:
4.1 在函数中修改列表
-
在函数中对这个列表所做的任何修改都是永久性的,这样可以高效的处理大量的数据;
-
编写一个函数,需要打印的设计存储在一个列表中,打印后移到另一个列表中;
def print_models(unprinted_designs, completed_models): """ 模拟打印每个设计,直到没有未打印的设计为止 打印每个设计后,都将其移到列表 completed_models 中 """ while unprinted_designs: current_designs = unprinted_designs.pop() # 模拟根据设计制作 3D 打印模型的过程 msg = "Printing model: " + current_designs print(msg) completed_models.append(current_designs) def show_completed_models(completed_models): """示打印好的所有模型""" msg = "\nThe following models have been printed: " print(msg) 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) msg = "\nafter modify: " print(msg) print(unprinted_designs)
- 上述中,将一个函数分开写,每个函数只负责一项具体的工作,这样使得程序更容易维护和扩展;
-
结果:
4.2 禁止函数修改列表
-
当不允许函数修改列表时,可以向函数传递列表的副本而不是原件;
-
使用切片法
[:]
创建列表副本; -
除非有充分的理由需要传递副本,不然应该将原始列表传递给函数;
-
因为让函数使用现成的列表可避免花时间和内存创建副本,从而提高效率;
4.3 练习
-
创建一个包含魔术师名字的列表,并将其传递给一个名为
show_magicians()
的函数,这个函数打印列表中每个魔术师的名字;
-
在上述例子中,编写一个名为
make_great()
的函数,对魔术师列表进行修改,在每个魔术师的名字后边加入字样the Great
,调用函数show_magicians()
确认列表的确变了。
-
将上述例子中的魔术师列表不进行改变,再次编写函数:
5. 传递任意数量的实参
- 有时候,预先不知道函数需要接受多少个实参,好在 Python 允许函数从调用语句中收集任意数量的实参;
5.1 使用任意数量的位置实参
-
当形参为
*names
,表示创建一个名为 names 的空元组,并将所有的值都封装到这个元组; -
编写一个名为
make_pizza()
的函数,它需要接受很多配料,所以形参设为空元组;def make_pizza(*toppings): """打印顾客点的所有配料""" print(toppings) make_pizza('pepperoni') make_pizza('mushrooms','green peppers', 'extra cheese')
-
结果:
-
不管收到的实参是多少个,这种语法都管用;
5.2 结合使用位置实参和任意数量实参
-
如果需要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后;
-
Python 先匹配位置实参和关键字实参,最后将余下的实参都收集到一个形参中;
-
在前面的函数中定义一个表示比萨尺寸的实参,必须将该形参放在形参
*toppings
的前面;def make_pizza(size, *toppings): """打印顾客点的所有配料""" msg = "\nMaking a " + str(size) + "-inch pizza with the following toppings:" print(msg) for topping in toppings: msg = "- " + topping print(msg) make_pizza(12, 'pepperoni') make_pizza(16, 'mushrooms','green peppers', 'extra cheese')
-
结果:
5.3 使用任意数量的关键字实参
-
当形参为
**names
,表示创建一个名为 names 的空字典,并将所有的名称-值都封装到这个字典中; -
编写一个名为
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='princetion', field='physics') print(user_profile)
-
结果为:
5.4 练习
-
编写一个函数,它接受顾客要在三明治中添加的一系列食材。这个函数只有一个形参(它收集函数调用中提供的所有食材),并打印一条消息,对顾客点的三明治进行概述;
# 定义函数,传入的实参为顾客为三明治添加的材料,以元组方式传入 def add_material(*material): '''判断传入的个数,输出不同的原材料''' num = len(material) '''传入 0 个材料,此处不能写'' ,只要有引号,就代表有东西,判定长度为1 ''' if num == 0: msg = "\nThe customer did not add any ingredients to the sandwich." print(msg) elif num == 1: msg = "\nThe customer added a material to the sandwich: " print(msg) for mater in material: msg = "- " + mater print(msg) elif num > 1: msg = "\nThe customer added the following ingredients to the sandwich: " print(msg) for mater in material: msg = "- " + mater print(msg) else: print("System error!") # 定义传入参数元祖,并调用函数 material = add_material() material = add_material('f') material = add_material('a', 'b', 'c')
-
结果:
6. 将函数存储在模块中
- 函数的优点之一就是可以将代码块与主程序分离;
- 将函数存储在被称为模块的独立文件中,再将模块导入主程序中;
- 模块是扩展名为
.py
的文件; import
语句允许在当前运行的程序文件中使用模块中的代码;- 将函数存储在独立的文件中,可隐藏程序代码的细节,将重点放在程序的高层逻辑上;
6.1 导入整个模块
-
创建一个包含函数
make_pizza()
的模块;def make_pizza(size, *toppings): """打印顾客点的所有配料""" msg = "\nMaking a " + str(size) + "-inch pizza with the following toppings:" print(msg) for topping in toppings: msg = "- " + topping print(msg)
-
在这个函数所在的目录创建另一个名为
make_pizza.py
的文件,使用这个文件导入刚才的模块;import pizza pizza.make_pizza(16, 'pepperoni') pizza.make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese')
-
结果为:
6.2 导入特定的函数
-
还可以导入模块中的特定函数,导入语法为:
from module_name import function_name
-
通过使用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:
from module_name import function_0, function_1
-
使用这中导入函数时,调用函数时无需使用句点:
6.3 使用 as 给函数指定别名
-
如果要导入的函数名称可能与程序中出现的名字有冲突,或者函数名字太长,可以指定简短独一无二的别名;
-
别名,函数的另一个名称,类似于外号;
-
指定别名的通用语法为:
from module_name import function_name as fn
-
下面给函数
make_pizza()
指定了别名mp()
;
6.4 使用 as 给模块指定别名
-
还可以给模块指定别名;
-
给模块指定别名的通用语法为:
import module_name as mn
-
下面给模块
pizza
指定别名为p
:
6.5 导入模块中的所有函数
-
使用
*
运算符可以导入模块中的所有函数; -
由于导入了每个函数,所以可以通过名称来调用每个元素,而无需使用句点表示法;
-
使用并非自己编写的大型模块时,最好不要采用这种导入方法;
-
如果模块中有函数与你的项目中使用的名称相同,可能会导致结果出乎意料;
-
导入所有函数的语法为:
from module_name import *
-
导入模块
pizza.py
中的所有函数: