函数参数
Python的函数参数非常灵活,除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数接口简单又强大。
位置参数
位置参数是最基本的参数形式,调用时按照参数定义的顺序依次传入。
┌─────────────────────────────────────────────────────────────────────┐ │ 位置参数调用过程 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ def power(x, n): # x, n 是位置参数 │ │ result = 1 │ │ while n > 0: │ │ result *= x │ │ n -= 1 │ │ return result │ │ │ │ 调用 power(5, 3): │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 传入顺序: │ │ │ │ │ │ │ │ power(5, 3) │ │ │ │ │ │ │ │ │ │ ▼ ▼ │ │ │ │ x=5, n=3 ──▶ 按位置匹配 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 位置参数示例
def power(x, n):
"""计算 x 的 n 次方"""
result = 1
while n > 0:
result *= x
n -= 1
return result
print(power(2, 3)) # 8 (2³ = 2*2*2)
print(power(5, 2)) # 25 (5² = 5*5)
默认参数
为参数设置默认值,调用时可以省略该参数。
┌─────────────────────────────────────────────────────────────────────┐ │ 默认参数工作原理 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ def power(x, n=2): # n=2 是默认参数 │ │ return x ** n │ │ │ │ 调用方式: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ power(5) ──▶ power(5, 2) 使用默认值 n=2 │ │ │ │ power(5, 3) ──▶ power(5, 3) 覆盖默认值 n=3 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 好处: 调用简单,大多数情况使用默认值的场景无需传参 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 默认参数示例
def greet(name, greeting="你好", punctuation="!"):
return f"{greeting},{name}{punctuation}"
# 省略默认参数
print(greet("张三")) # 你好,张三!
# 覆盖部分默认参数
print(greet("李四", "早上好")) # 早上好,李四!
# 覆盖所有默认参数
print(greet("王五", "晚上好", "~")) # 晚上好,王五~
默认参数必须指向不可变对象
如果默认参数使用可变对象,可能导致意外结果:
# 错误示例
def bad_func(items=[]):
items.append(1)
return items
print(bad_func()) # [1]
print(bad_func()) # [1, 1] -- 错误!应该每次都是 [1]
# 正确示例
def good_func(items=None):
if items is None:
items = []
items.append(1)
return items
print(good_func()) # [1]
print(good_func()) # [1] -- 正确!
可变参数 *args
使用*args可以接收任意数量的位置参数,这些参数在函数内部被封装成一个元组。
┌─────────────────────────────────────────────────────────────────────┐ │ *args 工作原理 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ def func(*args): │ │ print(args) │ │ │ │ 调用 func(1, 2, 3): │ │ │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ │ │ │ │ 传入: func(1, 2, 3) │ │ │ │ │ │ │ │ │ │ │ ▼ ▼ ▼ │ │ │ │ args = (1, 2, 3) ← 被打包成元组 │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 好处: 可以接收任意数量的参数,0个也可以 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 计算平方和
def calc_sum_of_squares(*args):
"""计算所有参数的平方和"""
total = 0
for n in args:
total += n * n
return total
print(calc_sum_of_squares(1, 2, 3)) # 14 (1+4+9)
print(calc_sum_of_squares(1, 2)) # 5 (1+4)
print(calc_sum_of_squares()) # 0
解包参数传入 *args
如果已经有了一个列表或元组,可以在调用时用*解包传入。
Python
def calc_sum_of_squares(*args):
return sum(n * n for n in args)
# 方式1: 直接传入
print(calc_sum_of_squares(1, 2, 3)) # 14
# 方式2: 解包列表/元组传入
numbers = [1, 2, 3]
print(calc_sum_of_squares(*numbers)) # 14 -- 等同于 calc_sum_of_squares(1, 2, 3)
关键字参数 **kwargs
使用**kwargs可以接收任意数量的关键字参数,这些参数在函数内部被封装成一个字典。
┌─────────────────────────────────────────────────────────────────────┐
│ **kwargs 工作原理 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ def func(**kwargs): │
│ print(kwargs) │
│ │
│ 调用 func(name="张三", age=25): │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 传入: func(name="张三", age=25) │ │
│ │ │ │ │ │
│ │ ▼ ▼ │ │
│ │ kwargs = {"name": "张三", "age": 25} ← 被打包成字典 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 特点: │
│ • 关键字参数是 key=value 形式 │
│ • 在函数内部收到的是 dict │
│ • 可以传入0个或任意多个 │
│ │
└─────────────────────────────────────────────────────────────────────┘
Python
# 打印用户信息
def print_user_info(**kwargs):
"""打印所有关键字参数"""
for key, value in kwargs.items():
print(f"{key}: {value}")
print("用户信息:")
print_user_info(name="张三", age=25, city="北京")
# 输出:
# name: 张三
# age: 25
# city: 北京
解包字典传入 **kwargs
如果已经有一个字典,可以在调用时用**解包传入。
Python
def print_user_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
# 方式1: 直接传入
print_user_info(name="张三", age=25)
# 方式2: 解包字典传入
user = {"name": "李四", "age": 30, "city": "上海"}
print_user_info(**user) # 等同于 print_user_info(name="李四", age=30, city="上海")
命名关键字参数
有时候需要限制关键字参数的名字,可以用命名关键字参数。
┌─────────────────────────────────────────────────────────────────────┐ │ 命名关键字参数语法 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 普通关键字参数 (可以传入任意名字): │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ def func(**kwargs): │ │ │ │ # kwargs 可以是任意 key │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 命名关键字参数 (限制只能传入特定名字): │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ def func(*, city, job): │ │ │ │ # 必须使用 city 和 job 作为参数名 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 或使用 *args 之后: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ def func(*args, city, job): │ │ │ │ # *args 之后的 city, job 是命名关键字参数 │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 命名关键字参数示例
def register(name, *, city, job):
print(f"姓名: {name}")
print(f"城市: {city}")
print(f"职业: {job}")
# 必须使用参数名传入
register("张三", city="北京", job="工程师")
# register("李四", "上海", "医生") # 错误!city和job必须用关键字参数
# 命名关键字参数也可以有默认值
def register2(name, *, city="北京", job):
print(f"{name} - {city} - {job}")
register2("李四", job="医生") # 李四 - 北京 - 医生 (使用city默认值)
参数组合图解
┌─────────────────────────────────────────────────────────────────────┐ │ Python 参数组合规则 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 定义顺序 (从左到右): │ │ │ │ 1. 位置参数 (如: x, y) │ │ 2. *args (可变位置参数) │ │ 3. 默认参数 (如: a=1, b=2) │ │ 4. 命名关键字参数 (如: *, city, job) │ │ 5. **kwargs (可变关键字参数) │ │ │ │ 简化记忆: │ │ ┌─────────────────────────────────────────────────────────────┐ │ │ │ 位置参数 → *args → 默认参数 → 命名关键字参数 → **kwargs │ │ │ └─────────────────────────────────────────────────────────────┘ │ │ │ │ 示例: │ │ def func(a, b, *args, c=1, d=2, **kwargs): │ │ # a, b : 位置参数 │ │ # *args : 可变位置参数 │ │ # c=1, d=2 : 默认参数 │ │ # **kwargs : 可变关键字参数 │ │ │ └─────────────────────────────────────────────────────────────────────┘
Python
# 完整参数组合示例
def complex_func(pos1, pos2, *args, default_val=100, named_key="default", **kwargs):
print(f"位置参数: pos1={pos1}, pos2={pos2}")
print(f"可变参数: {args}")
print(f"默认参数: default_val={default_val}")
print(f"命名关键字参数: named_key={named_key}")
print(f"其他关键字参数: {kwargs}")
# 调用示例
complex_func(1, 2, 3, 4, 5, default_val=999, named_key="test", extra=666)
# 输出:
# 位置参数: pos1=1, pos2=2
# 可变参数: (3, 4, 5)
# 默认参数: default_val=999
# 命名关键字参数: named_key=test
# 其他关键字参数: {'extra': 666}
解包调用
使用*解包列表,使用**解包字典。
Python
def point(x, y, z):
return (x, y, z)
# 用列表解包调用
coords = [1, 2, 3]
print(point(*coords)) # (1, 2, 3) -- 等同于 point(1, 2, 3)
# 用字典解包调用
params = {"x": 10, "y": 20, "z": 30}
print(point(**params)) # (10, 20, 30) -- 等同于 point(x=10, y=20, z=30)
# 组合使用
def calc(a, b, c=0, *args, **kwargs):
print(f"a={a}, b={b}, c={c}, args={args}, kwargs={kwargs}")
nums = [1, 2, 3]
opts = {"extra1": 100, "extra2": 200}
calc(*nums, **opts) # a=1, b=2, c=3, args=(), kwargs={'extra1': 100, 'extra2': 200}
章节测验
问题1: 下面代码的输出是什么?
def func(*args, **kwargs):
print(args, kwargs)
func(1, 2, a=3, b=4)
问题2: 以下哪个是定义命名关键字参数的方式?
问题3: 默认参数为什么不能使用可变对象?
问题4: 调用 func(*[1,2,3], ** {'a':4}) 相当于传入什么?
练习题
练习1: 改造乘积函数
将只接受两个参数的乘积函数,改造成可以接受任意数量参数的版本。
# 原函数
def mul(x, y):
return x * y
# 改造后可以这样调用:
# mul(2) = 2
# mul(2, 3) = 6
# mul(2, 3, 4) = 24
# mul(2, 3, 4, 5) = 120
练习2: 关键字参数收集
编写一个函数,接收姓名和其他任意信息,格式化为地址字符串。
格式: "姓名: xxx, 城市: xxx, 职业: xxx, ..."
# 如: format_address("张三", city="北京", age=25, job="工程师")
# 输出: "姓名: 张三, 城市: 北京, 职业: 工程师"
练习3: 参数解包调用
使用解包调用实现数组求和。
numbers = [1, 2, 3, 4, 5]
# 使用 *numbers 调用 sum_all 函数求和
🐍 Python 在线代码编辑器
📝 运行结果