Python学习笔记5—函数
1. 函数的定义
在Python中,函数可按下面这种方式定义:
1 | def greet_user(username): |
上面这段代码演示了最简单的函数结构,第一行代码使用关键字def来定义一个函数,def后面的greet_user(username)为函数名,username叫做函数的形参,而’jesse’叫做实参。紧跟在def greet_user(username):后面的所有缩进行构成了函数体。””” 显示简单的问候语 “””叫做文档字符串,描述的函数是做什么的,文档字符串用三引号括起,Python使用它们来生成有关程序中函数的文档。要使用这个函数,可调用它,依次指定函数名以及要传入括号中的参数。上面这段代码的输出为:
1 | Hello, Jesse! |
2. 传递参数
鉴于函数定义中可能包含多个形参,因此函数调用中也可能包含多个实参。向函数传递参数的方式很多,可使用位置实参,这要求实参的顺序与形参的顺序相同;也可使用关键字实参,其中每个实参由变量名和值组成;还可使用列表和字典。下面将一一介绍:
2.1 位置实参
在调用函数时,Python必须将函数调用中的每个实参都关联到函数定义中的一个形参,为此,最简单的关联方式是基于实参的顺序,这种关联方式被称为位置实参:
1 | def describe_pet(animal_type, pet_name): |
如上面的代码所示,在函数调用中,实参’hamster’存储在形参animal_type中,而实参’harry’存储在形参pet_name中,即实参按位置依次存储在两个形参中,最后的输出结果为:
1 | I have a hamster. |
2.2 关键字实参
关键字实参是传递给函数的名称—值对。在调用函数时,可在实参中将名称和值关联起来,这样在函数传递参数时就不会有参数传递错误的烦恼。关键字实参让你无需考虑函数调用中的实参顺序,还清楚地指出了函数调用中各个值的用途。如下所示为在调用函数时使用关键字实参:
1 | def describe_pet(animal_type, pet_name): |
在上面的代码中,在调用函数时使用了关键字实参,其输出结果与2.1中的结果一样,值得注意的是,关键字实参的顺序无关紧要,如下面两个函数的调用是等效的:
1 | describe_pet(animal_type='hamster', pet_name='harry') |
2.3 给形参指定默认值
编写函数时,可给每个形参指定默认值。在调用函数中给形参提供了实参时,函数将使用指定的实参值,否则将使用形参的默认值,如下所示:
1 | def describe_pet(pet_name, animal_type='dog'): |
在上面的代码中,调用函数时,没有给形参animal_type传入实参的值,因此形参animal_type将使用默认值’dog’作为要传入的实参值,其输出结果为:
1 | I have a dog. |
值得注意的是,在给形参指定默认值后,要是想用位置实参,则需要在函数定义时将指定了默认值的形参放在末尾,防止参数传递错误。
当然,你也可以在调用函数时传入实参以覆盖掉默认值,像下面这样:
1 | describe_pet(pet_name='harry', animal_type='hamster') |
此时,函数的输出结果为:
1 | I have a hamster. |
3. 返回值
3.1 返回简单值
在Python的函数中,可以使用return来返回一个值,如下所示:
1 | def get_formatted_name(first_name, last_name): |
在上面的代码中,调用函数get_formatted_name()时,会将整理好的姓名返回,并存储在变量musician中,其输出结果为:
1 | Jimi Hendrix |
3.2 将形参的默认值指定为空字符串以实现实参可选
有时候,需要让实参变成可选的,这样使用函数的人就只需在必要时才提供额外的信息,如下所示:
1 | def get_formatted_name(first_name, last_name, middle_name=''): |
在上面的代码中,将形参middle_name指定为空字符串,这样在调用函数时就可不向其传入参数。
3.3 返回字典或列表
函数可返回任何类型的值,包括字典和列表等较复杂的数据结构,如下所示:
1 | def build_person(first_name, last_name): |
其输出结果为:
1 | {'first': 'jimi', 'last': 'hendrix'} |
4. 传递列表
4.1 向函数传递列表
有时候,向函数传递列表很有用,这种列表包含的可能是名字、数字或更复杂的对象(如字典)。将列表传递给函数后,函数就能直接访问其内容,如下所示:
1 | def greet_users(names): |
在上面的代码中,将一个列表传入了函数,并在函数中对其进行遍历打印,其输出结果为:
1 | Hello, Hannah! |
4.2 在函数中修改列表
将列表传递给函数后,函数就可对其进行修改,在函数中对这个列表所做的任何修改都是永久性的,如下所示:
1 | def print_models(unprinted_designs, completed_models): |
在上面这段代码中,向函数print_models()传入一个具有三个元素的列表unprinted_designs和一个空列表completed_models,在函数中将列表unprinted_designs中的元素依次弹出,再依次存储在空列表completed_models中,调用print_models()函数后,原本具有三个元素的列表unprinted_designs变为空列表,而原本为空列表的completed_models则倒序存储有unprinted_designs中的元素,其输出结果如下所示:
1 | Printing model: dodecahedron |
4.3 禁止函数修改列表(使用列表切片副本)
将原列表当作实参传入函数时,如果在函数对其进行修改,则该修改是不可逆的,如果想要将列表传入函数,又要原列表不发生变化,则可选择将列表的切片副本传入函数,这样既能得到传递原列表相同的结果,又能保证原列表不发生变化,其形式如下所示:
1 | function_name(list_name[:]) |
5. 传递任意数量的实参
如果你预先不知道函数需要接受多少个实参,则可以采用下面的方法使函数能够传递任意数量的实参:
1 | def make_pizza(*toppings): |
形参名*toppings中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中,其输出结果为:
1 | ('pepperoni',) |
5.1 结合使用位置实参和任意数量实参
如果要让函数接收不同类型的实参,必须在函数定义中将接纳任意数量实参的形参放在最后。Python先匹配位置实参和关键字实参,再将余下的实参都收集到最后一个形参中,如下所示:
1 | def make_pizza(size, *toppings): |
基于上述函数定义,Python将收到的第一个值存储在形参size中,并将其他的所有值都存储在元组toppings中,其使出结果为:
1 | Making a 16-inch pizza with the following toppings: |
5.2 使用任意数量的关键字实参
有时候,需要接受任意数量的实参,但预先不知道传递给函数的会是什么样的信息。在这种情况下,可将函数编写成能够接受任意数量的键值对,如下面的代码所示:
1 | def build_profile(first, last, **user_info): |
函数build_profile()的定义要求提供名和姓,同时允许用户根据需要提供任意数量的名称—值对。形参**user_info中的两个星号让Python创建一个名为user_info的空字典,并将收到的所有名称—值对都封装到这个字典中。在这个函数中,可以像访问其他字典那样访问user_info中的名称—值对。
6. 将函数存储在模块中
函数的优点之一是使用它们可将代码与主程序分离。通过给函数指定描述性名称,可让主程序容易理解得多。进一步的,函数还可以存储在被称为模块的独立文件中,再将模块导入到主程序中。import语句允许再当前运行的程序文件中使用模块中的函数。导入模块的方法有很多种,下面将做一一介绍。
6.1 导入整个模块
要让函数是可导入的,得先创建模块。模块是扩展名为.py的文件,包含要导入到程序中的代码。下面来创建一个包含函数make_pizza()的模块,并将其保存在pizza.py中。
pizza.py
1 | def make_pizza(size, *toppings): |
接下来,在pizza.py所在的目录中创建另一个名为making_pizzas.py的文件,这个文件导入刚创建的模块,再调用make_pizza()两次:
making_pizzas.py
1 | import pizza |
Python读取这个文件时,代码行import pizza让Python打开文件pizza.py,并将其中所有的函数都复制到这个程序中。你看不到复制的代码,因为这个程序运行时,Python再幕后复制这些代码,你只需要知道,在making_pizza.py中,可以使用pizza.py中定义的所有函数。要调用被导入的模块中的函数,可指定导入的模块的名称pizza和函 数名make_pizza(),并用句点分隔它们。这些代码的输出与没有导入模块的原始程序相同:
1 | Making a 16-inch pizza with the following toppings: |
6.2 导入特定的函数
还可以导入模块中的特定函数,这种导入方法的语法如下:
1 | from module_name import function_name |
通过用逗号分隔函数名,还可根据需要从模块中导入任意数量的函数:
1 | from module_name import function_0, function_1, function_2 |
对于前面的making_pizzas.py示例,如果只想导入要使用的函数,代码将类似于下面这样:
1 | from pizza import make_pizza |
若使用这种语法,调用函数时就无需使用句点。由于我们在import语句中显式地导入了函数make_pizza(),因此调用它时只需指定其名称。
6.3 使用as给函数指定别名
如果要导入的函数的名称可能与程序中现有的名称冲突,或者函数的名称太长,可将函数指定为另一个好辨识的别名,如下所示:
1 | from pizza import make_pizza as mp |
6.4 使用as给模块指定别名
除了给函数指定别名外,还可以给模块指定别名,如下所示:
1 | import pizza as p |
6.5 导入模块中的所有函数
使用星号(*)运算符可让Python导入模块中的所有函数:
1 | from pizza import * |
当然在使用并非自己编写的大型模块时,不建议使用这种导入方法,它很可能会导致多个函数名称相同,从而互相覆盖的问题,正确的做法是,要么只导入你需要使用的函数,要么导入整个模块并使用句点表示法。