0%

第八章 函数

8.1 定义函数

1
2
3
4
5
def greet_user():
"""显示简单的问候语。"""
print("Hello!")

greet_user()

输出结果为:

1
Hello!

使用关键字def来告诉Python,要定义一个函数。这是函数定义,向Python指出了函数名,还可能在圆括号内指出函数为完成任务需要什么样的信息。

用三对引号括起来的文本称为文档字符串(docstring)的注释,描述了函数是做什么的。

8.1.1 向函数传递信息

1
2
3
4
5
def greet_user(username):
"""显示简单的问候语"""
print(f"Hello,{username}!")

greet_user("zhangsan")

输出结果为:

1
Hello,zhangsan!

8.1.2 实参和形参

在函数greet_user()的定义中,变量username是一个形参(parameter),即函数完成工作所需的信息。在代码greet_user(“zhangsan”)中,值“zhangsan”是一个实参(argument),即调用函数时传递给函数的信息。

8.2 传递实参

向函数传递实参的方式很多:

  • 使用位置实参,这要求实参的顺序与形参的顺序相同;
  • 使用关键字实参,其中每个实参都由变量名和值组成;
  • 使用列表字典

8.2.1 位置实参

调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参。为此,最简单的关联方式就是基于实参的顺序。这种关联方式称为位置实参

1
2
3
4
def describe_person(person_name, person_age):
print(f"my name is {person_name},i am {person_age} yesrs old~")

describe_person("zhangsan", 24)

输出结果为:

1
my name is zhangsan,i am 24 yesrs old~
  1. 多次调用函数

    1
    2
    3
    4
    5
    def describe_person(person_name, person_age):
    print(f"my name is {person_name},i am {person_age} yesrs old~")

    describe_person("zhangsan", 24)
    describe_person("lisi", 30)

    输出结果为:

    1
    2
    my name is zhangsan,i am 24 yesrs old~
    my name is lisi,i am 30 yesrs old~
  2. 位置实参的顺序很重要

    使用位置实参来调用函数时,如果实参的顺序不正确,结果可能出乎意料:

    1
    2
    3
    4
    def describe_person(person_name, person_sex):
    print(f"my name is {person_name},i am {person_sex}")

    describe_person("male", "zhangsan")

    输出结果为:

    1
    my name is male,i am zhangsan

8.2.2 关键字实参

关键字实参是传递给函数的名称值对。因为直接在实参中将名称和值关联起来,所以向函数传递实参时不会混淆。关键字实参让你必须考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。

1
2
3
4
def describe_person(person_name, person_sex):
print(f"my name is {person_name},i am {person_sex}")

describe_person(person_name = 'zhangsan', person_sex = 'male')

输出结果为:

1
my name is zhangsan,i am male

注意:使用关键字实参时,务必准确指定函数定义中的形参名。

8.2.3 默认值

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

1
2
3
4
def describe_person(person_name, person_sex = 'male'):
print(f"my name is {person_name},i am {person_sex}")

describe_person(person_name = 'zhangsan')

输出结果为:

1
my name is zhangsan,i am male

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

8.2.4 等效的函数调用

鉴于可混合使用位置实参,关键字实参和默认值,通常有多种等效的函数调用方式。

1
2
3
4
5
6
7
8
def describe_person(person_name, person_sex = 'male'):
print(f"my name is {person_name},i am {person_sex}")

describe_person("zhangsan")
describe_person(person_name = "lisi")
describe_person("wangwu","female")
describe_person(person_name = "zhaoliu", person_sex = 'female')
describe_person(person_sex = 'female', person_name = "liuqi")

输出结果为:

1
2
3
4
5
my name is zhangsan,i am male
my name is lisi,i am male
my name is wangwu,i am female
my name is zhaoliu,i am female
my name is liuqi,i am female

8.3 返回值

函数并非总是直接显示输出,它还可以处理一些数据,并返回一个或一组值。函数返回的值称为返回值

8.3.1 返回简单值

1
2
3
4
5
6
7
def get_formatted_name(first_name, last_name):
"""返回整洁的姓名"""
full_name = f"{first_name} {last_name}"
return full_name.title()

full_person_name = get_formatted_name('zhang', 'san')
print(full_person_name)

输出结果为:

1
Zhang San

8.3.3 返回字典

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

1
2
3
4
5
6
7
def build_person(first_name, last_name):
"""返回一个字典,其中包含有关一个人的信息。"""
person = {'first' : first_name, 'last' : last_name}
return person

full_person = build_person('zhang', 'san')
print(full_person)

输出结果为:

1
{'first': 'zhang', 'last': 'san'}

在条件测试中,None相当于False。

8.4 传递列表

1
2
3
4
5
6
def greet_users(names):
for name in names:
print(f"Hello {name}")

user_names = ['zhangsan', 'lisi', 'wangwu']
greet_users(user_names)

输出结果为:

1
2
3
Hello zhangsan
Hello lisi
Hello wangwu

8.4.1 在函数中修改列表

将列表传递给函数后,函数就可对其进行修改。在函数中对这个列表所做的任何修改都是永久性的。

1
2
3
4
5
6
7
def greet_users(names):
names.append('zhaoliu')

user_names = ['zhangsan', 'lisi', 'wangwu']
print(user_names)
greet_users(user_names)
print(user_names)

输出结果为:

1
2
['zhangsan', 'lisi', 'wangwu']
['zhangsan', 'lisi', 'wangwu', 'zhaoliu']

8.4.2 禁止函数修改列表

如果想在函数中操作列表而不影响原列表,可将列表的副本传递给函数,可以这样做:

1
function_name(list_name[:])

切片表示法[:]创建列表的副本。

虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由,否则还是应该将原始列表传递给函数。这是因为让函数使用现成的列表可以避免花时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此。

8.5 传递任意数量的实参

有时候,预先不知道函数需要接受多少个实参,好在Python允许函数从调用语句中收集任意数量的实参。

1
2
3
4
5
6
7
8
def greet_users(*names):
for name in names:
print(f"Hello {name}")
print('\n')

greet_users('zhangsan')
greet_users('zhangsan', 'lisi')
greet_users('zhangsan', 'lisi', 'wangwu')

输出结果为:

1
2
3
4
5
6
7
8
Hello zhangsan

Hello zhangsan
Hello lisi

Hello zhangsan
Hello lisi
Hello wangwu

形参名*names中的星号让Python创建一个名为names的空元组,并将收到的所有值都封装到这个元组中。

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

如果要让函数接受不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中。

1
2
3
4
5
6
7
def make_pizza(size, *toppings):
print(f"\nMaking a {size}-inch pizza with the following topping:")
for topping in toppings:
print(f"-{topping}")

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

输出结果为;l

1
2
3
4
5
6
7
Making a 16-inch pizza with the following topping:
-pepperoni

Making a 12-inch pizza with the following topping:
-mushrooms
-green peppers
-extra cheese

经常i看到通用形参名*args,它也收集任意数量的位置实参。

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

有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对——调用语句提供了多少就接受多少。

1
2
3
4
5
6
7
8
def build_profile(first, last, **user_info):
"""创建一个字典,其中包含我们知道的有关用户的一切。"""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info

user_profile = build_profile('zhang', 'san', location='chongqing', field='computer science')
print(user_profile)

输出结果为:

1
{'location': 'chongqing', 'field': 'computer science', 'first_name': 'zhang', 'last_name': 'san'}

形参**user_info中的两个星号让Python创建一个名为user_info的空字典,并将接收到的所有名称值对都放到这个字典中。

经常会看到形参名**kwargs,它用于收集任意数量的关键字实参。

8.6 将函数存储在模块中

使用函数的优点之一是可将代码块与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。还可以更进一步,将函数存储在称为模块的独立文件中,再将模块导入到主程序中。

8.6.1 导入整个模块

模块是扩展名为.py的文件,包含要导入到程序中的代码。

1
import module_name

只需要编写一条import语句并在其中指定模块名,就可在程序中使用该模块中的所有函数。如果使用这种import语句导入了名为module_name.py的整个模块,就可以使用下面的语法来使用其中的任何一个函数:

1
module_name.function_name()

8.6.2 导入特定的函数

还可以导入模块中的特定函数。

1
from module_name import function_name

通过用逗号分隔函数名,可根据需要从模块中导入任意数量的函数:

1
from module_name import function_0, function_1, function_2 

由于在import语句中显示地导入了函数,调用时只需指定其名称即可。

8.6.3 使用as给函数指定别名

如果要导入函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可指定简短而独一无二的别名:函数的另一个名称,类似于外号。要给函数取这种特殊外号,需要在导入它时指定。

1
from module_name import function_name as fn

8.6.4 使用as给模块指定别名

还可以给模块指定别名。

1
import module_name as mn

8.6.5 导入模块中的所有函数

使用星号(*)运算符可让Python导入模块中的所有函数:

1
from module_name import *

8.7 函数编写指南

应给函数指定描述性名称,且只在其中使用小写字母和下划线。

给形参指定默认值时,等号两边不要有空格。对于函数调用中的关键字实参,也应遵循这种约定。