一、Python 函数参数传递的核心概念
首先要明确:Python 中一切皆对象,参数传递的本质是「对象引用传递」(可以理解为 “传递的是对象的地址”),而不是简单的 “值传递” 或 “引用传递”。
关键区别:
- 不可变对象(如 int、str、tuple):修改时会创建新对象,原对象不变
- 可变对象(如 list、dict、set):修改时会直接操作原对象,原对象会变化
二、参数传递的 4 种基础形式
下面从简单到复杂,介绍 Python 函数参数的 4 种常用传递方式:
1. 位置参数(必选参数)
最基础的参数形式,必须按顺序传递,且数量要与函数定义一致。
python
运行
# 定义函数:计算两个数的和(a、b是位置参数)
def add(a, b):
return a + b
# 调用函数:按位置顺序传递参数
result1 = add(3, 5)
print(result1) # 输出:8
# 错误示例1:参数数量不匹配
# add(3) # 报错:缺少1个位置参数
# 错误示例2:顺序错误(结果不符合预期)
result2 = add(5, 3)
print(result2) # 输出:8(这里碰巧结果一样,换个场景就会出错)
2. 关键字参数
通过「参数名 = 值」的形式传递,无需按顺序,更清晰易懂。
python
运行
# 定义函数:计算矩形面积
def rectangle_area(length, width):
return length * width
# 关键字参数调用(顺序可任意)
area1 = rectangle_area(length=5, width=3)
area2 = rectangle_area(width=3, length=5)
print(area1) # 输出:15
print(area2) # 输出:15
# 混合使用:位置参数在前,关键字参数在后(必须遵守这个顺序)
area3 = rectangle_area(5, width=3)
print(area3) # 输出:15
# 错误示例:关键字参数不能在位置参数前面
# rectangle_area(width=3, 5) # 报错
3. 默认参数
定义函数时给参数指定默认值,调用时可省略该参数(使用默认值)。
python
运行
# 定义函数:打招呼(name是必选参数,greeting是默认参数)
def greet(name, greeting="Hello"):
return f"{greeting}, {name}!"
# 省略默认参数(使用默认值"Hello")
print(greet("Alice")) # 输出:Hello, Alice!
# 覆盖默认参数(使用自定义值)
print(greet("Bob", "Hi")) # 输出:Hi, Bob!
print(greet("Charlie", greeting="Good morning")) # 关键字形式覆盖
# 注意:默认参数必须放在位置参数后面
# def greet(greeting="Hello", name): # 报错
⚠️ 重要提醒:默认参数的默认值最好是不可变对象(如 int、str、None),不要用 list、dict 等可变对象(会导致意外结果)。
python
运行
# 错误示例:默认参数用可变对象list
def bad_func(a, b=[]):
b.append(a)
return b
print(bad_func(1)) # 输出:[1]
print(bad_func(2)) # 输出:[1, 2](不是预期的[2])
# 正确示例:用None作为默认值,在函数内创建可变对象
def good_func(a, b=None):
if b is None:
b = [] # 每次调用都创建新的list
b.append(a)
return b
print(good_func(1)) # 输出:[1]
print(good_func(2)) # 输出:[2](符合预期)
4. 可变参数(*args 和 **kwargs)
用于处理不确定数量的参数,灵活度极高。
(1)*args:接收任意数量的「位置参数」,打包成元组(tuple)
python
运行
# 定义函数:计算任意多个数的和
def sum_all(*args):
print(type(args)) # 输出:<class 'tuple'>
total = 0
for num in args:
total += num
return total
# 调用:传递任意数量的位置参数
print(sum_all(1, 2, 3)) # 输出:6
print(sum_all(10, 20, 30, 40)) # 输出:100
print(sum_all()) # 输出:0(允许传递0个参数)
# 解包传递(用*把列表/元组解包成位置参数)
nums = [1, 2, 3, 4, 5]
print(sum_all(*nums)) # 输出:15(等价于sum_all(1,2,3,4,5))
(2)**kwargs:接收任意数量的「关键字参数」,打包成字典(dict)
python
运行
# 定义函数:打印用户信息
def print_user(**kwargs):
print(type(kwargs)) # 输出:<class 'dict'>
for key, value in kwargs.items():
print(f"{key}: {value}")
# 调用:传递任意数量的关键字参数
print_user(name="Alice", age=25, city="Beijing")
# 输出:
# name: Alice
# age: 25
# city: Beijing
# 解包传递(用**把字典解包成关键字参数)
user_info = {"name": "Bob", "age": 30, "city": "Shanghai"}
print_user(**user_info)
# 输出:
# name: Bob
# age: 30
# city: Shanghai
(3)*args 和 **kwargs 同时使用
通常用于接收所有类型的参数,顺序必须是:位置参数 → *args → 关键字参数 → **kwargs
python
运行
def flexible_func(a, b, *args, c=0, **kwargs):
print(f"必选参数a: {a}, b: {b}")
print(f"可变位置参数args: {args}")
print(f"默认关键字参数c: {c}")
print(f"可变关键字参数kwargs: {kwargs}")
# 调用示例
flexible_func(1, 2, 3, 4, c=5, d=6, e=7)
# 输出:
# 必选参数a: 1, b: 2
# 可变位置参数args: (3, 4)
# 默认关键字参数c: 5
# 可变关键字参数kwargs: {'d': 6, 'e': 7}
三、参数传递的核心规则总结
- 传递本质:Python 是「对象引用传递」,不可变对象修改会创建新对象,可变对象修改会影响原对象。
- 参数顺序:定义函数时必须遵循:
位置参数 → 默认参数 → *args → 关键字参数 → **kwargs。 - 调用规则:调用时,位置参数必须在关键字参数前面。
- 灵活传递:*args 接收任意位置参数(元组),**kwargs 接收任意关键字参数(字典),支持解包传递。
- 默认参数陷阱:默认参数不要用可变对象(list、dict 等),改用 None 在函数内初始化