文章目录
- 前言
- 一、函数定义
- 二、传递实参
- 三、返回值
- 四、传递列表
- 五、传递任意数量实参
- 六、把函数存储在模块中
- 七、函数定义指南
- 总结
前言
在Python中,函数是一段可重用的代码块,用于执行特定任务。函数可以通过定义关键字 def
来创建,并且通常具有一个名称以便调用。函数可以接受输入参数,在函数体内部进行处理,并通过return语句返回结果。Python函数支持默认参数值、可变数量的参数以及关键字参数等多种形式的参数传递方式,使得函数更加灵活和强大。此外,Python还支持匿名函数(lambda函数)以及高阶函数,后者允许函数作为参数传递给其他函数或作为其他函数的结果返回。从本章开始,内容逐渐复杂了,如果你像笔者一样有过其他语言的开发经验,那倒没什么问题,否则就应该动手多写代码,培养自己的编程思维。
一、函数定义
下面的是一个简单的代码示例。定义了一个获取用户名称的函数,函数内部是一个while循环。首先检测用户从控制台输入的用户名,判断受否全部为英文字母,是则返回名称,否则继续循环等待输入姓名。
def get_user_name():
while True:
name = input("What is your name? ")
if name.isalpha():
return name # 返回有效的用户名并结束函数
else:
print("Please enter a valid name")
# 调用函数
user_name = get_user_name()
print(f"Hello, {user_name}!")
打印结果证实代码的逻辑
向函数传递信息
对上面的代码简单修改下:定义一个函数,接受一个参数,从控制台输入自己的名字,然后调用函数将其打印出来。
def get_user_name(username):
print(f"hello,{username}")
# 调用函数
input_name = input("请输入你的名字:")
get_user_name(input_name)
实参和形参
使用上述代码来解释:函数get_user_name的方法参数username是 形参
——函数完成工作所需要的一项信息。代码get_user_name(input_name)中的input_name就是 实参
——调用函数时传递给函数的信息。
二、传递实参
函数不会只有一个参数。当然你可以用一个参数比如使用字典把所有参数都包含了,传递给函数内部再解析获取参数,但这有时候又很没必要,尤其是参数只有几个不多的情况。有时把函数定义成可接受多个参数是十分必须要的,而且是常见的。
向函数传递实参的方式很多,可使用 位置实参
,这要求实参的顺序与形参的顺序相同;也可使用 关键字实参
,其中每个实参都由变量名和值组成;还可使用 列表和字典
。
位置实参
在Python中,位置实参(positional arguments)指的是在调用函数时按参数的位置顺序传递给函数的实参。也就是说,实参与形参之间的对应关系是基于它们在函数调用时出现的顺序。
位置实参的基本概念
- 顺序匹配:调用函数时,实参按顺序传递给形参。
- 数量匹配:实参的数量必须与形参的数量相匹配,除非有默认值或其他类型的参数。
- 错误处理:如果实参的数量不匹配,Python会抛出一个 TypeError 异常。
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('hamster', 'harry') # 使用位置实参
结果如下,如果参数少传了就会报错,少传参数时,默认按照函数参数的顺序复制
使用位置实参传递参数时,需要注意的就是参数顺序和参数个数,保证这两点即可。
关键字实参
在Python中,关键字实参(keyword arguments)是一种在调用函数时指定参数的方式,它允许你通过参数名称而不是位置来传递实参。这种方式提供了更大的灵活性,因为你不需要关心参数的顺序,只需要确保每个形参都有对应的实参即可。
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这一特性,笔者觉得很好,JavaScript的函数也支持这点。遗憾的是Java不支持这一特性,希望未来能够支持吧!
def describe_pet(animal_type, pet_name='harry'):
"""显示宠物的信息。"""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet(animal_type='hamster') # 使用关键字实参
注意:使用默认值时,在形参列表中必须先列出没有默认值的形参,再列出有默认值的形参。这让Python依然能够正确地解读位置实参。
向下面这样,把没有默认值的参数放在后面,IDE就会给出错误提示
注意:函数形参上的默认值,只有在实参没有传时才使用,如果实参传了则以实参为主。
def describe_pet(animal_type='hamster', pet_name='harry'):
"""显示宠物的信息。"""
print(f"\nI have a {animal_type}.")
print(f"My {animal_type}'s name is {pet_name.title()}.")
describe_pet('dog') # 使用关键字实参
三、返回值
函数要么有返回值,要么没有返回值。函数在帮我们处理完一大段逻辑后可以返回我们想要的结果。
返回简单值
def get_formatted_name(first_name,last_name):
full_name = first_name+''+last_name
return full_name.title()
musician = get_formatted_name('jimi','hendrix')
print(musician)
实参可选
什么是让实参可选呢?就是函数有多个参数,但是不同的调用者需求不同,有的甚至没有那么多的实参可传,所以我们可以给那些非必传的参数添加上默认值。这样在调用函数的时候就可以根据需要决定是否传入对应的实参了。
def get_formatted_name(first_name,last_name,middle_name=''):❶
"""返回整洁的姓名"""
if middle_name:❷
full_name = first_name+''+middle_name+''+last_name
else:❸
full_name = first_name+''+last_name
return full_name.title()
musician = get_formatted_name('jimi','hendrix')
print(musician)
musician = get_formatted_name('john','hooker','lee') ❹
print(musician)
函数返回字典
以下函数内部定义了一个有关学生成绩的字典,并返回该字典。
def create_student_grades():
"""
创建并返回一个包含学生姓名和成绩的字典。
"""
student_grades = {
'Alice': 88,
'Bob': 75,
'Charlie': 92,
'David': 80,
'Eva': 95
}
return student_grades
# 调用函数并打印返回的字典
grades = create_student_grades()
print(grades)
结合使用函数和while循环
下面是一个代码示例,展示了如何在一个 while 循环中调用一个函数来不断询问用户输入,直到用户输入一个有效的整数为止。
def get_integer_input(prompt):
"""
请求用户输入一个整数,直到输入有效为止。
"""
while True:
try:
user_input = input(prompt)
return int(user_input)
except ValueError:
print("Invalid input! Please enter an integer.")
number = get_integer_input("Enter an integer: ")
print(f"You entered the number: {number}")
四、传递列表
这就是笔者前面说到的,向函数中传递复杂数据类型。有时候我们需要对一个列表进行处理,处理逻辑我们封装到函数中,向函数传递列表参数即可。
languages_list = ["java", "python", "c++", "c", "c#", "javascript", "php", "ruby", "swift", "go"]
def upper_case(languages):
# 使用索引来修改列表中的元素
for language in languages:
print(language)
upper_case(languages_list)
一段简单代码,向函数中传递一个列表,循环并打印每个元素
在函数中修改列表
这段代码展示了,向函数中传如一个列表,遍历列表中的每个元素,并把每个元素的首字母转成大写的修改操作。
languages_list = ["java", "python", "c++", "c", "c#", "javascript", "php", "ruby", "swift", "go"]
def upper_case(languages):
# 使用索引来修改列表中的元素
for i in range(len(languages)):
languages[i] = languages[i].title()
return languages
print(upper_case(languages_list))
笔者为什么没有继续使用 for language in languages:
这种遍历方式呢?因为这种结构是遍历列表,不会直接对原列表中的元素进行修改。想要达到在遍历同时修改原列表的元素,采用索引的访问方式。
禁止函数修改列表
有时我们向一个函数中传入一个列表,但我们不希望原始列表被修改,怎么做?很简单,只需要向函数中传递副本即可。列表副本使用方式在前面的文章中介绍过使用[:]即可。
languages_list = ["java", "python", "c++", "c", "c#", "javascript", "php", "ruby", "swift", "go"]
def upper_case(languages):
# 使用索引来修改列表中的元素
for i in range(len(languages)):
languages[i] = languages[i].upper()
print(languages[i])
upper_case(languages_list[:])
print(languages_list)
透过结果可以看到,内部如何处理传入的列表副本都不会影响到原始列表。虽然向函数传递列表的副本可保留原始列表的内容,但除非有充分的理由需要传递副本,否则还是应该将原始列表传递给函数,因为让函数使用现成列表可避免花时间和内存创建副本,从而提高效率,在处理大型列表时尤其如此。
五、传递任意数量实参
有时候我们调用一个函数或者方法时,不确定到底要传多少个参数,可能只有调用的时候才会知道。Java中类似的写法是 ...args
,在Python中当然也有类似的支持语法了,使用 *args
表示。
简单示例如下
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
print(toppings)
make_pizza('pepperoni')
make_pizza('mushrooms','green peppers','extra cheese')
形参名*toppings中的星号让Python创建一个名为toppings的空元组,并将收到的所有值都封装到这个元组中。
代码稍微改动下,内部使用for循环打印接收到的参数
def make_pizza(*toppings):
"""打印顾客点的所有配料"""
for topping in toppings:
print(topping)
make_pizza('pepperoni')
make_pizza('mushrooms','green peppers','extra cheese')
可以看到,我们无论传入几个参数,都能正确处理。
位置实参和任意数量实参组合使用
def make_pizza(size, *toppings):
"""
打印制作比萨的信息及配料。
参数:
size -- 比萨的尺寸
*toppings -- 配料列表
"""
print(f"\n正在制作一个 {size} 寸的比萨,配料包括:")
for topping in toppings:
print(f"- {topping}")
# 调用函数
make_pizza(16, '培根', '蘑菇', '洋葱')
make_pizza(12, '菠萝', '火腿')
注意
- 任意数量的形参应该放在最后位置
- 此种方式不能使用关键字实参
使用任意数量的关键字实参
有时候我们需要接受任意数量的实参,但我们不知道传递给函数的会是什么样的内容。我们可以使用任意数量的关键字实参,即字典形式来接收参数。
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中的名称—值对。
学习和了解这些形参实参的概念是十分必要的,整体不是很难理解,但应多加练习,因为我们会经常阅读到他人写的代码。只有通过不断的动手练习,才能加深印象,不至于看到代码时发懵。
六、把函数存储在模块中
函数可以做到逻辑的复用,还可以做到隐藏实现细节。Python中可以把一些函数放入到独立的模块中,主程序在使用时可以导入对应的模块,调用函数。
导入整个模块
先定义一个简单模块my_module.py,其中有几个函数
def multiply(a, b):
"""
计算两个数的乘积。
参数:
a : int 或 float -- 第一个数字
b : int 或 float -- 第二个数字
返回:
int 或 float -- 两数相乘的结果
"""
return a * b
def average(numbers):
"""
计算列表中所有数字的平均值。
参数:
numbers : list -- 包含数字的列表
返回:
float -- 数字的平均值
"""
return sum(numbers) / len(numbers)
def is_palindrome(text):
"""
检查字符串是否为回文。
参数:
text : str -- 需要检查的字符串
返回:
bool -- 如果字符串是回文则返回 True,否则返回 False
"""
return text == text[::-1]
def fibonacci(n):
"""
计算斐波那契数列的第 n 项。
参数:
n : int -- 要计算的斐波那契数列的项数
返回:
int -- 斐波那契数列的第 n 项
"""
if n <= 0:
raise ValueError("n must be a positive integer")
elif n == 1:
return 0
elif n == 2:
return 1
else:
a, b = 0, 1
for _ in range(n - 2):
a, b = b, a + b
return b
再定义一个单独的example.py
# 导入整个模块
import my_module
# 示例调用两数乘积函数
product = my_module.multiply(3, 5)
print(product)
导入的关键字为 import
,example.py中导入了整个模块 my_module,注意不要加.py。在代码中调用了导入模块的一个函数multiply()。
导入模块的模板如下:
import module_name
具体使用导入模块函数的模板如下:
module_name.function_name()
导入特定的函数
有时我们不想导入整个模块,只想导入部分函数怎么办?
导入特定函数模板如下
from module_name import function_name
导入模块中任意数量模块函数的模板如下:
from module_name import function_0,function_1,function_2
下面的代码,演示了两种导入函数的方法
# 导入特定函数
from my_module import multiply
# 导入部分数量的函数
from my_module import average, is_palindrome
# 示例调用两数乘积函数
product = multiply(3, 5)
print(product)
# 示例调用平均值函数
average_value = average([1, 2, 3, 4, 5])
print(average_value)
# 示例调用判断回文函数
is_palindrome_result = is_palindrome("racecar")
print(is_palindrome_result)
使用as给导入的函数指定别名
模板如下:
from module_name import function_name as alia_name
这个比较简单,给出一个示例,不再解释
# 导入特定函数
from my_module import multiply as mt
# 示例调用两数乘积函数
product = mt(3, 5)
print(product)
使用as给模块指定别名
模板如下:
import module_name as alia_name
# 导入特定函数
import my_module as module
# 示例调用两数乘积函数
product = module.multiply(3, 5)
print(product)
导入模块中的所有函数
模板如下:
from module_name import *
# 导入模块的所有函数
from my_module import *
# 示例调用两数乘积函数
product = multiply(3, 5)
print(product)
和导入整个模块差不多
七、函数定义指南
- 应给函数指定描述性名称,且只在其中使用小写字母和下划线。
- 每个函数都应包含简要地阐述其功能的注释,该注释应紧跟在函数定义后面,并采用文档字符串格式。
- 给形参指定默认值时,等号两边不要有空格:
- def function_name(parameter_0,parameter_1=‘default value’)
- 如果程序或模块包含多个函数,可使用两个空行将相邻的函数分开,这样将更容易知道前一个函数在什么地方结束,下一个函数从什么地方开始。
- 所有的import语句都应放在文件开头,唯一例外的情形是,在文件开头使用了注释来描述整个程序。
总结
本章介绍了Python中函数的基本定义和使用方式,如何定义参数和传递参数。还介绍了导入模块和模块中函数的方法。掌握函数的定义和使用,我们才能写出可复用的代码。