2017年12月5日
8.1 定义函数
8.1.1 简单的函数实例:
def greet_user():
"""显示简单的问候语"""
print("Hello!")
greet_user()
8.1.2 实参与形参:
- 形参(parameters):函数完成其工作所需的一项信息
- 实参(arguments):调用函数时传递给函数的信息,调用函数时,实参的值被存储在形参中
8.2 传递实参
鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递实参的方式很多,包括位置实参,关键字实参以及列表和字典
8.2.1 位置实参
我们调用函数,将每个实参关联到一个实参时,最简单的关联方式是基于实参的顺序,这种方式被称为位置实参(positional arguments)*
注意:位置实参的顺序很重要
def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print(" ".join(["My", animal_type + "'s name is",\
pet_name.title()+"."]))
describe_pet('hamster', 'harry') # 位置实参
8.2.2 关键字实参
关键字实参(keyword argument)是传递给函数的名称-值对,你直接在实参中将名称和值关联起来。
def describe_pet(animal_type, pet_name):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print(" ".join(["My", animal_type + "'s name is",\
pet_name.title()+"."]))
describe_pet(animal_type='hamster', pet_name='harry') # 关键字实参
调用函数时,我们向Python明确地指出了各个实参对应的形参。
注意:使用关键字实参时,务必准确地指定函数定义中的形参名。
8.2.3 默认值
编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,Python将使用指定的实参值;否则,将使用形参的默认值。
使用默认值可简化函数调用,还可清楚地指出函数的典型用法。
def describe_pet(pet_name, animal_type='dog'):
"""显示宠物的信息"""
print("\nI have a " + animal_type + ".")
print(" ".join(["My", animal_type + "'s name is",\
pet_name.title()+"."]))
describe_pet('harry') # 位置实参
如果要描述的动物不是小狗,可使用类似于下面的函数调用:
describe_pet(pet_name='harry', animal_type='Hamster')
注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参,这让Python依然能够正确地解读位置实参。
8.2.5 避免实参错误
提供的实参多于或少于函数完成其工作所需的信息时,将出现实参不匹配错误。
应该给变量和函数指定描述性名称,这将使Python的traceback提供的错误信息更有帮助。
8.3 返回值
函数返回的值被称为返回值。在函数中,可使用return语句将值返回到调用函数的代码行。
函数可返回任何类型的值,包括列表和字典等较复杂的数据结构,例如下面的函数接受姓名的组成部分,并且增加了作为可选形参的年龄:
def build_person(first_name, last_name, age=''):
"""返回一个包含人信息的字典"""
person = {'first name': first_name, 'last name': last_name}
if age:
person['age'] = age
return person
musician = build_person('jimi', 'hendrix', age=27)
print(musician)
8.4 传递列表
8.4.1 在函数中修改列表
将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的,这让你能够高效地处理大量的数据。
def confirm_users(unconfirmed_users, confirmed_users):
"""
模拟验证每个用户,直到没有未验证的用户为止
验证每个用户后,都将其移动到列表confirmed_users中
"""
while unconfirmed_users:
current_user = unconfirmed_users.pop() # 使用pop来提取列表中元素
confirmed_users.append(current_user)
print(current_user.title() + " has been confirmed.")
def show_confirmed_users(confirmed_users):
"""显示所有已验证的用户"""
print("\nThe following users have been confirmed:")
for confirmed_user in confirmed_users:
print(confirmed_user)
unconfirmed_users = ['alice', 'brian', 'candace']
confirmed_users = []
confirm_users(unconfirmed_users, confirmed_users)
show_confirmed_users(confirmed_users)
相比于没有使用函数的版本,这个程序更容易扩展和维护。
此外,这个程序还演示了这样一种理念,即每个函数都应只负责一项具体的工作。
8.4.2 禁止函数修改列表
有时候,需要禁止函数修改列表,为了解决这个问题,可向函数传递列表的副本而不是原件;这样函数所做的任何修改都只影响副本,而丝毫不影响原件。
function_name(list_name[:])
注意:虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由,否则还是应该将原始列表传递给函数,因为让函数使用现成列表可避免耗费时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此。
8.5 传递任意数量的实参
有时候,你预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参。
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
在上面的make_pizza
函数中,形参名*toppings
中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。
8.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(12, 'pepperoni')
make_pizza(16, 'mushrooms', 'green peppers', 'extra cheese')
基于上述函数定义。Python将收到的第一个值存储在形参size
中,并将其他的所有值都存储在元组toppings
中。
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)
函数build_profile()
的定义要求提供名和姓,同时允许用户根据需要提供任意数量的名称-值对。形参**user_info
中的两个星号让Python创建一个名为user_info的空字典,并将收到的所有名称-值对都封装到这个字典中。在这个函数中,可以像访问其他字典那样访问user_info
中的名称-值对。
8.6 将函数存储在模块中
可将函数存储在被称为模块的独立文件中,再将模块导入到主程序中。import
语句允许在当前运行的程序文件中使用模块中的代码。
8.6.1 导入整个模块
import module_name # 导入模块
module_name.function_name() # 使用模块中的函数
这就是一种导入方法:只需要编写一条import
语句并在其中指定模块名,就可在程序中使用该模块中的所有函数。
8.6.2 导入特定的函数
你还可以导入模块中的特定函数,这种导入方法的语法如下:
from module_name import function_name
通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:
from module_name import function_0, function_1, function_2
若使用这种语法,调用函数时就无需使用.
句点。
8.6.3 使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突,可指定简短而独一无二的别名。要给函数指定这种特殊外号,需要在导入它时这样做。
from module_name import function_name as fn
8.6.4 使用as给模块指定别名
你还可以给模块指定别名。通过给模块指定简短的别名,让你能够更轻松地调用模块中的函数。
import module_name as mn # 给模块制定别名
mn.function_name() # 使用函数中的模块
8.6.5 导入模块中的所有函数
使用星号(*
)运算符可让Python导入模块中的所有函数:
from pizza import *
make_pizza(12, 'pepperoni')
make_pizza(16, 'mushrooms', 'green peppers', 'extra cheese')
import
语句中的星号让Python将模块pizza中的每个函数都复制到这个程序文件中。由于导入了所有函数,可通过名称来调用每个函数,而无需使用句点表示法。然而,使用并非自己编写的大型模块时,最好不要采用这种导入方法:如果模块中有函数的名称与你的项目中使用的名称相同,可能导致意想不到的结果:Python可能遇到多个名称相同的函数或变量,进而覆盖函数,而不是分别导入所有的函数。
最佳的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。这能让代码更清晰,更容易阅读和理解。
8.7 函数编写指南
编写函数时,需要牢记几个细节。应给函数指定描述性名称,且只在其中使用小写字母和下划线。描述性名称可帮助你和别人明白代码想要做什么。给模块命名时也应遵循上述约定。
每个函数都应包含简要地描述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串格式。加入说明文档以后,使用funcion_name.__doc__
(注意:函数名后不包含空格)就可以查看函数function_name
的说明文档。
文档良好的函数让其他程序员只需阅读文档字符串中的描述就能够使用它:他们完全相信代码如描述的那样运行;只要知道函数的名称、需要的实参以及返回值的类型,就可以在自己的程序中使用它。
给实参指定默认值时,等号两边不要有空格:
def function_name(parameter_0, parameter_1='default value')
对于函数调用中的关键字实参,也应遵循这种约定:
function_name(value_0, parameter_1='value')
PEP8建议代码行的长度不要超过79字符。如果形参很多,导致函数定义的长度超过了79字符,可在函数定义中输入左括号后按回车键,并在下一行按两次Tab键,从而将形参列表和只缩进一层的函数体区分开来。
def function_name(
parameter_0, parameter_1, parameter_2,
parameter_3, parameter_4, parameter_5):
function body...
如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始。
所有的import语句都应放在文件开头。