函数可以将程序拆分成独立的几部分,使程序趋于简单化。
1.1、定义和调用函数
使用关键字def 来告诉python定义一个函数,向python指出了函数名,也还可以在括号内指出函数为完成任务需要什么样的信息。
而调用一个函数,只需让python指定函数名以及括号内的必要信息即可。
def display_message(): # 函数定义
"""display a message about what I am learning.""" # 文档字符串的注释,描述函数是做什么的,由三引号组成,python用来生成有关程序中函数的文档
msg = "I'm learning to store code in functions."
print(msg)
display_message() # 函数调用
----------
I'm learning to store code in functions.
1.1.1、向函数参数传递信息
函数在定义时,可在括号内指定任意参数,在调用时再给其指定一个值,调用函数时,可将值传递给参数:
def great_user(username): # 指定参数(形参)
print('Hello ' + username.title() + '!')
great_user('edward') # 指定值并传递给参数(实参)
-------
Hello Edward!
1.1.2、形参和实参
在1.1.1中,变量username是一个形参,它是函数定义时括号内的参数,是函数完成其工作所需的一项信息。
值'edward' 是一个实参,它是函数调用时括号内的参数,是调用函数时传递给函数的信息。
在great_user('edward')中,将参数edward传递给函数great_user(),并存储在形参user_name中。
1.2、传递实参
函数定义可能包含多个形参,调用时也可以有多个实参,传递实参的方式很多,有位置参数法,(要求形参实参的顺序一致)意义,关键字参数。
1.2.1、位置实参
位置实参要求实参与形参的顺序一一对应,切记不能对应错误,另一方面,一个函数可以多次调用,只需再次指定实参即可。
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print('\nI have a ' + animal_type + '.')
print("My " + animal_type + "'s name is " + pet_name.title() + '.')
describe_pet('hamster', 'harry') # 实参与形参顺序对应,宠物种类对应animal_type、名字对应pet_name
describe_pet('dog', 'baby') # 二次调用
1.2.2、关键字实参
位置参数固然有其优点,但只适合形参比较少的程序,若形参有很多,传递实参时就容易显得杂乱无章,关键字实参将名称与值关联起来,向形参传递实参时就不会混淆。
def describe_pet(animal_type, pet_name): # 形参
"""显示宠物信息"""
print('\nI have a ' + animal_type + '.')
print("My " + animal_type + "'s name is " + pet_name.title() + '.')
describe_pet(animal_type= 'hamster', pet_name= 'harry') # 将实参直接存储到形参变量中,不管顺序如何变化都不影响结果
describe_pet(pet_name= 'baby', animal_type= 'dog')
1.2.3、默认值
在函数定义时可以给形参直接指定个默认值,如果与其关联的实参给有默认值的形参提供了值,则用提供的值,否则用默认值。
需要注意的是,在调用函数时,只有一个实参,且是位置实参,那么没有指定默认值的形参要在有默认值的形参前面,否则实参将会以位置参数的形式传递给第一个形参:
def describe_pet(pet_name, animal_type='dogs'): # 有默认值在没有默认值的形参后面
"""显示宠物信息"""
print('\nI have a ' + animal_type + '.')
print("My " + animal_type + "'s name is " + pet_name.title() + '.')
describe_pet('willie') # 这个实参将传递给第一个形参,第二个形参使用默认值
describe_pet(animal_type='dog', pet_name='baby') # 有默认值的形参使用实参
-------------------
I have a dogs.
My dogs's name is Willie.
I have a dog.
My dog's name is Baby.
1.2.4、等效的函数调用
鉴于可混合位置实参、关键字实参以及默认值,通常有多种调用方式,以下便是等效的函数调用方式,其结果一致:
# 一只名为Willie的小狗
describe_pet('willie') # 结果一致
describe_pet(pet_name='willie')
# 一只名为Harry的仓鼠 # 结果一致
describe_pet('harry', 'hamster')
describe_pet(pet_name='harry', animal_type='hamster')
describe_pet(animal_type='hamster', pet_name='harry')
1.2.5、避免实参错误
在使用函数时,若遇到实参不匹配时,python会提示我们需要为哪些提供实参,如果这个函数存储在一个独立的文件中,那么需要打开这个文件查看函数的代码:
def describe_pet(pet_name, animal_type):
print('My ' + animal_type + "'s name is " + pet_name.title() + '.')
describe_pet() # 这个函数缺少实参,python会提示我们需要为哪个形参提供实参
Traceback (most recent call last):
File "C:/Users/hj/PycharmProjects/untitled1/function_1.py", line 55, in <module>
describe_pet()
TypeError: describe_pet() missing 2 required positional arguments: 'pet_name' and 'animal_type'
1.3、返回值
函数并非总是直接显示输出,相反,它也处理一些数据,并返回一个或一组值,返回的 值称为返回值,它可以返回列表、元组和字典,可使用return 语句将值返回到调用函数的代码行,返回值可以让你能够将程序的大部分繁重工作移到函数中去完成,从而简化程序。
1.3.1、返回简单值
def get_formatted_name(first_name, last_name):
"""返回完整姓名"""
full_name = first_name + ' ' + last_name # 将完整姓名存储到变量full_name中
return full_name.title() # 将full_name的值返回到函数调用行
musician = get_formatted_name('jimi', 'hendrix') # 在调用返回值的函数时,需要提供一个变量,用于存储返回值
print(musician) # 打印变量
------
Jimi Hendrix
1.3.2、让实参变得可选
有时需要让实参变得可选,这样就能存储额外的信息,可使用默认值来使实参可选:
# 拓展get_formatted_name(),使其还处理中间名
def get_formatted_name(first_name, last_name, middle_name=''): # 指定middle_name的默认值为空
"""返回完整姓名"""
if middle_name: # 判断调用函数时是否给middle_name指定了实参,python将非空字符串认为True
full_name = first_name + ' ' + middle_name + ' ' + last_name
else:
full_name = first_name + ' ' + last_name
return full_name.title()
musician = get_formatted_name('jimi', 'hooker', 'lee')
print(musician)
musician = get_formatted_name('jimi', 'hooker') # 调用函数时,如果没有middle_name的实参就会导致程序错误,因此可以指定形参middle_name的默认值为空,当有实参时则用实参的值,没有的时候就是空字符串,这样不会奔溃程序
print(musician)
-----
Jimi Lee Hooker
Jimi Hooker
1.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) # 音乐家
-------
{'first': 'jimi', 'hendrix': 'hendrix'}
返回字典,可以轻易拓展这个函数,使其接受可选值,如年龄、中间名、职业等你想存储的其他信息:
def buile_person(first_name, last_name, age=''): # 新增了age可选项
"""返回一个字典,其中包含人名、年龄"""
person = {'first': first_name, 'last': last_name}
if age:
person['age'] = age # 将键-值对添加到字典person中
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)
-------
{'first': 'jimi', 'last': 'hendrix', 'age': 27}
1.3.4、结合使用函数和 while 循环
def get_formatted_name(first_name, last_name):
"""返回完整姓名"""
full_name = first_name + ' ' + last_name
return full_name.title()
while True:
print("\nTell me what's your name: ")
print("(enter 'q' to quit)") # 设置循环退出条件
f_n = input('First_name: ') # 接收用户输入信息
if f_n == 'q':
break
l_n = input('Last_name: ')
if l_n == 'q':
break
formatted = get_formatted_name(f_n, l_n)
print('\nHello: ' + formatted + '!')
--------------------
Please tell me your name:
(Enter 'q' to quit.)
First_name: Li
Last_name: La
Hello Li La!
1.4、传递列表
向函数传递列表,这种列表包含的可能是名字、数字或更复杂的对象(字典),将列表传递给函数后,函数就能直接访问其内容,可以提高处理列表的效率。
1.4.1、在函数中修改列表
将列表传递给函数后,可以对其进行修改,修改后是永久性的,可以高效第处理大量数据。
一家为用户提交设计制作的3D打印模型公司,需要打印的设计存储在一个列表中,打印后移到另一个列表中:
def printed_models(unprinted_designs, completed_models): # 定义一个函数,2个形参,一个为未打印的设计,一个为完成的模型
"""
模拟打印每个设计,直到没有未打印的为止
打印每个设计后,将其移动completed_models中
"""
while unprinted_designs: # 循环(只要unprinted_designs不为空循环继续)
current_design = unprited_designs.pop() # 将列表中所有元素弹出
# 模拟根据设计制作3D打印模型的过程
print('Printing model: ' + current_design)
completed_models.append(current_design) # 将弹出的元素添加到空列表completed_models中
def show_completed_models(completed_models): # 定义一个函数,用以处理所有打印好的模型
"""显示打印好的模型"""
print('\nThe folloing models have been printed:')
for completed_model in completed_models: # 遍历打印好的模型列表completed_models
print(completed_model)
unprinted_designs = ['iphone case', 'robot pendant', 'dodecahedron'] # 定义未打印的设计的列表
completed_models = [] # 定义已经打印好的模型为空列表
printed_models(unprinted_designs, completed_models) # 调用函数,将列表传递给主函数
show_printed_models(completed_models)
--------------
Printing model: dodecahedron
Printing model: robot pendant
Printing model: iphone case
The following models have been printed:
dodecahedron
robot pendant
iphone case
1.4.2、禁止函数修改列表
有时需要禁止修改列表,因为修改是永久的,如上个列子,修改未打印的设计中元素,将其中的元素全部移到已经打印的模型列表中,原来的列表就变为空,为解决这个问题,可以在调用函数时,只传递列表的副本(切片)而不是原列表:
# 在上个列子中,可以在调用函数,传递列表时,只传递列表的切片
# 格式为:function_name(list_name[:])
printed_models(unprinted_design[:], completed_models)
但是除非有充分理由需要保留原列表,否则尽量避免使用,因为可以使函数避免花时间去和内存去创建副本,从而提高效率,在处理大型列表尤其如此。
1.5、传递任意数量的实参
有时不知需要接受多少个实参,好在python可以允许函数调用语句中收集任意数量的实参:
def make_pizza(*topping): # 形参名*topping的星号让python创建一个名为tooping的空元组,并将传递来的实参全部封装在这个元组中
"""打印顾客点的所有配料"""
print(topping)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
----
('pepperoni') # 封装成一个元组
('mushrooms', 'green peppers', 'extra cheese')
1.5.1、结合使用位置实参和任意数量实参
如果要让函数接受不同类型的实参,必须将接受任意数量的实参放置在位置参数后面,python先匹配位置和关键字实参,再匹配任意数量的实参:
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(20, 'mushrooms', 'green peppers', 'extra cheese')
------------
Making a 16-inch pizza with the following toppings:
- pepperoni
Making a 20-inch pizza with the following toppings:
- mushrooms
- green peppers
- extra cheese
1.5.2、使用任意数量的关键字实参
有时,要接受任意数量的实参,但不知道传递给函数的会是什么信息,这种情况下,可以将函数写成能够接受任意数量的键-值对,调用语句提供了多少就接受多少:
下面列子,你将接受用户信息,但不确定什么信息,函数build_profile()接受名和姓,同时还接受任意数量的关键字实参:
def build_profile(first, last, **user_info): # **user_info两个星号让python创建一个名为user_info的空字典,并将接受到所有名称-值对都封装在其中
"""创建一个字典,其中包含我们知道的有关用户的一切"""
profile = {} # 首先创建一个空字典,用以存储用户的名和姓
profile['first_name'] = first # 将名和姓添加到字典profile中
profile['last_name'] = last
for key, value in user_info.items(): # 遍历user_info这个字典
profile[key] = value # 将每个键-值对添加到字典profile中
return profile # 返回字典profile到函数调用行,并把它存储到变量user_profile中
user_profile = build_profile('albert', 'einstein',
location='princeton',
field='physics') # 传入实参(调用函数)
print(user_profile)
在上面列子中,返回的字典包含了用户的名和姓,已经求学的地方和所学专业,调用这个函数时,不管额外提供了多少键-值对,它都能正确处理。
练习
编写一个函数,将一辆汽车的信息存储在一个字典中,这个函数始终接受制造商和型号,还接受关键字实参,调用这个函数,提供必不可少的信息以及两个名称-值对,如颜色、选择配件等:
def make_car(manufacturer, model, **options): # **options接受任意数量的关键字实参,创建一个字典并将信息存储在其中
"""做一个字典代表一辆车"""
car_dict = { # 创建一个字典能代表一辆车,其中有必不可少的 制造商型号等信息
'manufacturer': manufacturer.title(),
'model': model.title(),
}
for option, value in options.items(): # 遍历字典options
car_dict[option] = value # 将其他信息诸如颜色等添加到字典car_dict中
return car_dict # 返回car_dict字典
car = make_car('subaru', 'outback', color='blue', tow_package=True)
print(car)
my_car = make_car('honda', 'accord', year=1991, color='white', headlights='popup')
print(my_car)
1.6、将函数存储在模块中
函数可以将代码块与主程序分离,这样使程序更容易理解,还可以进一步将函数存储到独立的文件中(模块),再将模块带入主程序中,这样就可以将重点放在程序的高层逻辑上,可以与其他程序员共享这个文件而不是程序。
1.6.1、导入整个模块
模块的拓展名为.py的文件,包含要导入到程序中的代码,下面来演示再另一个程序中调用第一个模块里的函数。
创建一个名为pizza.py的模块,里面包含make_pizza() 的函数,再在模块pizza.py所在目录创建一个名为making_pizza.py的文件,在这个文件中导入刚创建的模块,再调用make_pizza() 函数,调用模块中函数格式:(模块名.函数名,如pizza.make_pizza() ):
# pizza.py 模块
def make_piza(size, *topping): # make_pizza() 函数
"""概述要制作的披萨"""
print('\nMaking a ' + str(size) + '-inch pizza with the following topping:')
for topping in toppings:
print('- ' + topping)
# making_pizza.py 文件
import pizza # 导入pizza模块
pizza.make_pizza(16, 'fish') # 调用pizza.py模块中的make_pizza()函数
pizza.make_pizza(20, 'egg', 'meat', 'fish')
----------------
Making a 12-inch pizza with the following topping:
- meat
- fish
- egg
1.6.2、导入特定的函数:
import 只是一种导入方式,还可以导入模块中的特定函数,其格式为:
from modul_name import function_name # from 模块名 import 函数名
from modul_name import function_0, function_1, function_2 # 导入任意数量的函数
对于1.6.1的列子也可以使用这种导入方式:
from pizza import make_pizza
make_pizza(16, 'fish') # 因为import语句显示地导入了函数名,所以调用时不需要指定其名称
1.6.3、使用 as 给函数指定别名
如果要导入的函数与现有函数名有冲突,或者函数名过长,可指定别名(用以代替函数名,类似于外号),只需在导入模块时,(from modul_name import funtion_name as fn):
from pizza import make_pizza as mp # 给函数指定别名
mp(16, 'fish') # 调用时可以使用别名
1.6.4、给模块指定别名
也可以给模块指定别名,其格式为:( import module_name as mn )
import pizza as p
p.make_pizza(16, 'fish')
1.6.5、导入模块中所有函数
使用星号(*)可以让python导入模块中的所有函数,其格式为:
from modul_name import *
在大型模块中,最好不要采用这种导入方式,如果模块中有函数的名称与你的项目有相同的,可能导致意想不到的结果,python处置名称相同的函数或者变量时,会进行覆盖,最佳做法是,只导入需要的函数,要么整个模块并使用句点表示法(即modul_name.function_name() )。
1.7、函数编写指南
- 函数名,应取具有描述性的名称(即与实现函数功能相关的名称),使用小写字母与下划线,模块名亦如此,这样便于理解。
- 每个函数应有注释,紧跟函数定义后面,采用文档字符串格式。
- 给形参指定默认值时,等号两边不用有空格,实参亦如此。
- PEP 8 建议代码行长度不超过79个字符,如果形参很多,超过79个字符,可以在函数定义时输入左括号后按回车键,并在下一行按两次Tab键,实质上多数编辑器都能自动实现参数对齐,如下:
def function_name(
parameter_0, parameter_1, parameter_2,
parameter_3, parameter_4, parameter_5
):
- 如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在哪结束,下一个函数在哪开始。
- 所有的import 语句都应在开头,唯一的列外的情形时,在文件开头使用了注释来描述整个程序。
网友评论