趋近智
if 语句for 循环with 自动关闭文件from ... importself 参数说明finally 块:清理操作在定义函数时,您学习了如何指定在函数调用时接受参数的形参。然而,有时您希望函数形参在调用方未提供值时,能有一个“标准”或“默认”值。这使得函数更为灵活,因为调用方只需为那些与默认值不同的形参提供实参即可。
Python 允许您直接在函数定义中为形参设定默认值。
要为一个形参赋予默认值,您可以在 def 语句中使用赋值运算符(=)为其赋值。
def greet(name, greeting="Hello"):
"""打印问候语。
Args:
name: 要问候的人的名字。
greeting: 要使用的问候语(默认为“Hello”)。
"""
print(f"{greeting}, {name}!")
# 调用函数
greet("Alice") # 使用默认问候语
greet("Bob", "Good morning") # 覆盖默认问候语
输出:
Hello, Alice!
Good morning, Bob!
在此示例中,greeting 形参的默认值为 "Hello"。
greet("Alice") 时,我们只提供了 name 实参。Python 检测到 greeting 实参缺失,并自动使用其默认值 "Hello"。greet("Bob", "Good morning") 时,我们提供了两个实参。值 "Good morning" 会覆盖 greeting 的默认值。定义带有默认实参的函数时,一个重要规则是:所有带有默认值的形参必须位于所有不带默认值的形参之后。
为什么?当您使用位置实参(不指定形参名称,按顺序传递的实参)调用函数时,Python 从左到右将值与形参匹配。如果一个非默认形参位于默认形参之后,Python 将无法判断所提供的实参是针对非默认形参的,还是旨在覆盖前面形参的默认值。
正确示例:
def create_user(username, is_active=True, permissions="read"):
print(f"创建用户: {username}")
print(f"活跃状态: {is_active}")
print(f"权限: {permissions}")
错误示例: 这将导致 SyntaxError。
# SyntaxError: 非默认参数位于默认参数之后
# def create_user_incorrect(is_active=True, username, permissions="read"):
# pass
您可以以多种方式调用带有默认实参的函数:
只提供必需实参: Python 会为其余实参使用默认值。
create_user("charlie")
# 输出:
# 创建用户: charlie
# 活跃状态: True
# 权限: read
按位置提供实参: 值会按顺序覆盖默认值。
create_user("david", False) # 覆盖 is_active,使用默认权限
# 输出:
# 创建用户: david
# 活跃状态: False
# 权限: read
create_user("eve", False, "admin") # 覆盖两个默认值
# 输出:
# 创建用户: eve
# 活跃状态: False
# 权限: admin
使用关键字提供实参: 这允许您覆盖特定的默认值,而不考虑它们的位置(只要满足必需实参)。这种方式通常更清晰。
create_user("frank", permissions="write") # 只覆盖权限
# 输出:
# 创建用户: frank
# 活跃状态: True
# 权限: write
create_user(username="grace", is_active=False) # 使用关键字实参
# 输出:
# 创建用户: grace
# 活跃状态: False
# 权限: read
默认实参常用于:
verbose=False 标志用于启用额外输出。timeout=30 秒。tolerance=0.001。有一个不明显但重要的细节需要注意:默认实参值只在函数定义时评估一次,而不是每次函数调用时都评估。对于数字、字符串或元组等不可变类型,这没有问题。然而,如果您使用列表或字典等可变类型作为默认值,那么所有依赖默认值的调用都将使用同一个对象。这可能导致意料之外的结果。
考虑这个示例:
def add_item(item, my_list=[]):
"""向列表中添加一个项。(演示可变默认值问题)"""
my_list.append(item)
print(f"列表当前为: {my_list}")
print("第一次调用:")
add_item("apple")
print("\n第二次调用:")
add_item("banana") # 意外地添加到第一次调用时的列表!
print("\n第三次调用,提供一个列表:")
add_item("cherry", ["start"]) # 当提供一个列表时,按预期工作
输出:
第一次调用:
列表当前为: ['apple']
第二次调用:
列表当前为: ['apple', 'banana']
第三次调用,提供一个列表:
列表当前为: ['start', 'cherry']
请注意第二次调用 add_item("banana") 并没有从一个空列表开始。它使用了在第一次调用中被修改的同一个列表。这很少是预期的行为。
标准解决方案:
处理可变默认值的常规方法是使用 None 作为默认值,然后如果形参为 None,则在函数内部创建一个新的可变对象。
def add_item_safe(item, my_list=None):
"""安全地向列表中添加一个项。"""
if my_list is None:
my_list = [] # 如果未提供列表,则创建一个新列表
my_list.append(item)
print(f"列表当前为: {my_list}")
print("第一次调用(安全):")
add_item_safe("apple")
print("\n第二次调用(安全):")
add_item_safe("banana") # 从一个新的空列表开始
print("\n第三次调用,提供一个列表(安全):")
add_item_safe("cherry", ["start"])
输出:
第一次调用(安全):
列表当前为: ['apple']
第二次调用(安全):
列表当前为: ['banana']
第三次调用,提供一个列表(安全):
列表当前为: ['start', 'cherry']
使用 None 作为占位符默认值,并在函数内部创建可变对象,能确保每次依赖默认值的调用都获得一个全新的实例,从而避免调用之间产生意外的副作用。
默认实参值是一项让您的函数更方便、更具适应性的功能,但请记住可变默认值和不可变默认值之间的区分,以避免常见问题。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造