Python中的函数
函数是带名字的代码块,用于完成具体的工作。如果要执行函数定义的特定任务,可以调用函数完成。
接下来完成一个简单的函数定义:
python
def greet_user():
"""显示简单的问候语"""
print("Hello!")
greet_user()
执行结果:

需要关注函数的以下几点:
def是用来定义函数的关键字。- 第二行双引号引起的内容是称为文档字符串的注释(可以包含多行),描述了这个函数做了什么。
print("Hello!")是函数执行的内容。
函数的传参
函数不是一个完全封闭的代码块,也可以接收传递进来的信息进行处理,能够与整个程序形成很好的串接。那么函数是如何接收传递过来的信息?信息又是通过什么方式传递进去的?涉及到两个概念,一个是函数的形参 ,一个是函数的实参。
形参是函数提供的接口,用于声明函数可以接收并处理什么类型的数据。
实参是传递给函数的信息,可以是一个常量,可以是一个变量。
python
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(animal_type = "hamster", pet_name = "harry")
执行结果:

这里animal_type和pet_name就是函数的形参,hamster和harry就是函数的实参。
那么接下来就会想到一个问题,上面函数调用的时候为什么一定要这么写,不写形参是什么,直接传递两个字符串是否可以?实际上函数参数的传递方式有两种,一种是实参与形参位置一一对应(位置实参),另一种就是上述方式通过形参的名值与实参相关联(关键字实参)。
python
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet("dog", "willie")
describe_pet(animal_type = "hamster", pet_name = "harry")
执行结果:

这里有需要注意的是,如果是通过位置实参方式传参,就必须保证位置是一一对应的,如果位置发生了变化,那么形参相应接收到的值也是不一样的。
python
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet("willie", "dog")
执行结果:

当使用关键字实参传参方式的时候,必须保证形参的名值与定义的时候是一模一样的,这里就不再演示了。如果通过关键字实参方式传参则无需关注实参和形参的位置是否是一一对应的了。
python
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(pet_name = "harry", animal_type = "hamster")
执行结果:

既然了解了如何将参数传递进函数当中,那么如果在特定场景中某个参数就是没有值,那是不是可以不进行传参操作?如果发生参数数量和实参数量不对应的情况(不管多与少)是会报错。
python
def describe_pet(animal_type, pet_name):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet('dog')
执行结果:

那么其中一种解决方式是可以按需给形参设置默认值,如果给形参设置了默认值,那么如果没有给该形参赋值,那么函数处理的时候就按照默认值进行处理。
python
def describe_pet(pet_name, animal_type='dog'):
"""显示宠物信息"""
print(f"\nI have a {animal_type}")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet('willie')
执行结果:

在设置默认值的时候有个很关键的注意点,就是当某个形参设置了默认值以后,后面所有形参都必须设置默认值,否则就会报错。
传递任意数量的实参
如果预先不知道函数需要接收多少个实参,这个时候通过改变函数形参定义方式实现。比如在制作披萨的时候,不同的客户所需要的配料是不同的,这个时候就会不确定会传入的多少个实参,这个时候就可以通过下述方式来实现:
python
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms', 'green peppers', 'extra cheese')
执行结果:

通过观察可以发现当多个参数传入函数的时候,变成了一个元组,这里需要注意,即使传入一个实参也是会变成一个元组。
当结合位置实参使用的时候,接收任意数量实参的形参定义的时候尽量放在最后,否则无法确定任意数量实参是在什么位置传递结束的。如果接收任意数量实参的形参在定义的时候没有放在最后,那么后续定义的形参在传参时都必须使用关键字实参的传参方式。
python
def make_pizza(size, *toppings, pizza_type):
"""概述要制作的披萨"""
print(f"\nMaking a {size}-inch pizza with the following toppings:")
for topping in toppings:
print(f"- {topping}")
print(f"The pizza's type is {pizza_type}")
make_pizza(16, 'pepperoni', pizza_type='big')
make_pizza(12, 'mushrooms', 'green peppers', 'extra cheese', pizza_type='small')
执行结果:

当然倘若传参的时候不仅不确定实参的数量,还不知道实参的含义或类型,这个时候可以通过接收关键字实参来实现,接收到函数当中会变成一个多键值对的字典。
python
def build_profile(first, last, **user_info):
"""创建一个字典,其中包含我们知道的有关用户的一切"""
user_info['first_name'] = first
user_info['last_name'] = last
return user_info
user_profile = build_profile('albert', 'einsten',
location='princeton',
field='physics')
print(user_profile)
执行结果:

函数返回值
要想观测函数处理结果不仅仅可以通过print函数打印出来,还可以通过函数的返回值来观察函数处理结果,通过return语句实现。
python
def get_formatted_name(first_name, last_name):
"""返回标准格式的姓名"""
full_name = f"{first_name} {last_name}"
return full_name.title()
musician = get_formatted_name('jimi', 'hendrix')
print(musician)
执行结果:

当调用有返回值的函数的时候,如果要接收函数的返回值需要提供一个变量来赋值。
模块
了解了函数,就可以引入模版的概念了。使用函数的有点是可以将主程序和代码块分离,那么我们可以将分离出来的代码块放入一个独立的文件当中,使用时再将其进行导入。通过将函数存储到独立文件当中,可以隐藏程序代码细节,将重点放在程序的高层逻辑上,还能使其在不同的程序中都可以使用这部分共用代码块。
那么如何导入整个模块
python
import module_name
使用的时候只需按照模块名称 + . + 函数名称即可引用该模块中的对应函数。
python
module_name.function_name(arg1, arg2)
也可以直接导入模块中的特定函数
python
from module_name import function_name_1, function_name_2
如果只导入特定函数,在使用的时候就可以不用带模块名称
python
function_name_1(arg1, arg2, arg3)
function_name_2(arg1, arg2)
也可以通过as关键字给模块或者函数重命名
python
# 模块重命名
import module_name as md
# 函数重命名
from module_name import function_name_1 as func1
同时也可以使用*导入模块中的所有函数
python
from module_name import *
通过这种方式引入函数,也可以直接通过函数名称调用,不需要加模组名称,但是如果主程序当中声明了相同名称的函数就可能会出现问题,所以如果引入模块中的所有函数的时候,最好使用模块引入的方式来声明。