def
关键字的奥秘与实践def
关键字是 Python 中定义函数的基石。函数是组织代码、提高复用性、实现模块化的基本单元。深入理解 def
的使用及其背后的机制,对于编写高质量、可维护的 Python 代码至关重要。
第一章:函数定义的基础:def
语法与基本结构
使用 def
关键字定义函数的基本语法如下:
def function_name(parameter1, parameter2, ...):
"""
Docstring: 函数的说明文档,解释函数的功能、参数、返回值等。
这部分是可选的,但强烈推荐编写。
"""
# 函数体:包含执行任务的代码块
# 函数体必须缩进
statement1
statement2
# ...
return result # 可选的返回值
1.1 def
关键字
def
是 define 的缩写,用于告诉 Python 解释器你正在定义一个函数。当解释器看到 def
时,它会创建一个函数对象,并将该对象赋值给后面指定的函数名。
1.2 函数名 (function_name
)
示例 1.2.1:合法的和非法的函数名
def calculate_total(): # 合法且符合规范
pass
def processData(): # 合法,但不太符合 Python 规范 (CamelCase)
pass
def _helper_function(): # 合法,以下划线开头通常表示内部或私有函数 (约定)
pass
# def 123_function(): # 非法,以数字开头
# pass
# def my-function(): # 非法,包含连字符
# pass
1.3 圆括号 ()
紧跟在函数名后面的圆括号用于包含函数的参数。即使函数没有参数,也必须保留这对圆括号。
示例 1.3.1:有参数和无参数的函数定义
def say_hello(): # 无参数
print("Hello!")
def greet(name): # 一个参数
print(f"Hello, {
name}!")
def add(a, b): # 两个参数
return a + b
1.4 参数列表 (parameter1, parameter2, ...
)
1.5 冒号 :
函数定义的最后必须加上冒号,它标志着函数头的结束和函数体的开始。
1.6 函数体 (Indented Block)
{}
来标识代码块的归属。示例 1.6.1:缩进示例
def example_function():
print("This line is inside the function.") # 缩进 4 个空格
x = 10 # 缩进 4 个空格
if x > 5:
print("x is greater than 5.") # 在 if 块内,需要更多缩进
print("This line is also inside the function, but outside the if block.") # 回到函数体的主缩进级别
# print("This line is outside the function.") # 没有缩进,不属于函数体
1.7 文档字符串 (Docstring
)
"""Docstring content"""
。help()
函数或访问函数的 __doc__
属性来查看文档字符串。示例 1.7.1:文档字符串示例
def multiply(a, b):
"""
这个函数计算两个数字的乘积。
Args:
a: 第一个数字。
b: 第二个数字。
Returns:
a 和 b 的乘积。
"""
return a * b
print(help(multiply)) # 查看函数的文档
print(multiply.__doc__) # 访问 __doc__ 属性
良好的文档字符串是编写易于理解和维护的代码的重要组成部分。
1.8 return
语句
return
语句用于从函数中退出,并将一个值返回给函数调用者。return
语句。在这种情况下,函数执行完毕后,默认返回 None
。return
语句可以出现在函数体的任何位置。一旦执行到 return
,函数会立即终止。return
后面可以跟一个表达式或多个表达式(此时会返回一个元组)。示例 1.8.1:return
语句的使用
def greet_and_return(name):
print(f"Hello, {
name}!")
return f"Greeting completed for {
name}" # 返回一个字符串
result = greet_and_return("Alice")
print(f"函数返回的结果: {
result}")
def no_return_function():
print("This function does not have a return statement.")
result2 = no_return_function()
print(f"没有 return 语句的函数返回的结果: {
result2}") # 输出 None
def early_exit(x):
if x < 0:
print("输入是负数,提前退出。")
return "Error: Negative input" # 遇到负数时提前返回
print("输入是非负数,继续计算。")
return x * 2
print(early_exit(5))
print(early_exit(-3))
def return_multiple_values():
return 1, "hello", [3, 4] # 返回一个元组
a, b, c = return_multiple_values()
print(f"返回的多个值: a={
a}, b={
b}, c={
c}")
理解 return
如何控制函数执行流程和传递结果是掌握函数使用的基础。
第二章:函数参数的深度解析
Python 函数的参数机制非常灵活,支持多种参数类型,可以应对各种调用场景。
2.1 位置参数 (Positional Arguments)
位置参数是最常见的参数类型。在函数调用时,实参的值按照它们在参数列表中的位置顺序与函数定义中的形参一一对应。
def describe_pet(animal_type, pet_name):
"""显示宠物的信息。"""
print(f"我有一只 {
animal_type}。")
print(f"它的名字叫 {
pet_name}。")
print("--- 位置参数调用示例 ---")
describe_pet('仓鼠', '哈姆太郎') # '仓鼠' 对应 animal_type, '哈姆太郎' 对应 pet_name
# 如果位置不匹配,会导致错误
# describe_pet('哈姆太郎', '仓鼠') # 顺序错误,输出 "我有一只 哈姆太郎。"
# describe_pet('狗') # 参数数量不够,会引发 TypeError
# describe_pet('猫', '加菲', '加多宝') # 参数数量过多,会引发 TypeError
调用时提供的实参数量和顺序必须与函数定义中的形参数量和顺序严格匹配(除非使用了其他参数类型)。
2.2 关键字参数 (Keyword Arguments)
关键字参数允许你在调用函数时,通过形参的名称来指定实参的值,而不必关心实参的顺序。
def describe_pet(animal_type, pet_name):
"""显示宠物的信息。"""
print(f"我有一只 {
animal_type}。")
print(f"它的名字叫 {
pet_name}。")
print("\n--- 关键字参数调用示例 ---")
describe_pet(animal_type='仓鼠', pet_name='哈姆太郎') # 使用关键字指定
describe_pet(pet_name='哈姆太郎', animal_type='仓鼠') # 改变顺序也可以,因为指定了关键字
结合使用位置参数和关键字参数时,需要注意:所有位置参数必须出现在关键字参数之前。
# 合法的组合调用
describe_pet('仓鼠', pet_name='哈姆太郎')
# 非法的组合调用 (位置参数在关键字参数之后)
# describe_pet(pet_name='哈姆太郎', '仓鼠') # SyntaxError: positional argument follows keyword argument
2.3 默认参数 (Default Arguments)
你可以在函数定义时为参数指定默认值。如果调用函数时没有为该参数提供实参,就会使用默认值。带有默认值的参数必须放在没有默认值的参数后面。
def describe_pet(pet_name, animal_type='狗'): # animal_type 具有默认值,放在 pet_name 后面
"""显示宠物的信息,默认动物类型是狗。"""
print(f"我有一只 {
animal_type}。")
print(f"它的名字叫 {
pet_name}。")
print("\n--- 默认参数调用示例 ---")
describe_pet('旺财') # animal_type 使用默认值 '狗'
describe_pet('咪咪', '猫') # 为 animal_type 提供实参,覆盖默认值
describe_pet(pet_name='小黄') # 使用关键字参数调用,animal_type 使用默认值
describe_pet(pet_name='大白', animal_type='兔') # 使用关键字参数覆盖默认值
# 错误的默认参数位置
# def greet(name='Guest', message): # 非法:没有默认值的参数在有默认值的参数之后
# print(f"{message}, {name}!")
默认参数的陷阱 (可变默认参数): 默认值在函数定义时只计算一次。如果默认值是可变对象(如列表、字典或集合),并且在函数体内修改了这个默认值,那么在后续调用中,这个修改会保留下来。
# 错误的示例:使用可变对象作为默认参数
def append_to_list(value, my_list=[]):
my_list.append(value)
return my_list
print("\n--- 可变默认参数陷阱示例 ---")
print(append_to_list(1)) # 输出: [1]
print(append_to_list(2)) # 输出: [1, 2] - 意料之外!使用了同一个列表
print(append_to_list(3)) # 输出: [1, 2, 3]
# 正确的做法是使用不可变对象(如 None)作为默认值,并在函数体内检查并创建新的可变对象
def append_to_list_correct(value, my_list=None):
if my_list is None:
my_list = [] # 在函数被调用时创建新的列表
my_list.append(value)
return my_list
print("\n--- 正确处理可变默认参数 ---")
print(append_to_list_correct(1)) # 输出: [1]
print(append_to_list_correct(2)) # 输出: [2] - 现在每次调用都使用新的列表
print(append_to_list_correct(3)) # 输出: [3]
这是 Python 中一个常见但重要的陷阱,务必引起注意。
2.4 任意数量的位置参数 (*args
)
有时你不知道函数会接收多少个位置参数。可以使用 *args
来收集任意数量的位置参数,它们会被存储在一个元组中。
def sum_all(*args):
"""计算所有位置参数的和。"""
print(f"\n--- sum_all 接收到参数: {
args} (类型: {
type(args