Python函数参数进阶:吃透*args和**kwargs,灵活处理可变参数
在Python编程中,函数参数的灵活运用是提升代码复用性和扩展性的关键。除了基础的位置参数、默认参数,可变参数(*args和**kwargs)更是实战开发中的“高频工具”——它们能让函数接收任意数量的参数,轻松应对不确定输入场景(比如批量处理数据、兼容多版本接口等)。本文将从实际需求出发,用通俗的语言+实战案例,带你彻底掌握*args和**kwargs的用法,内容兼顾新手理解与搜索引擎优化。
一、为什么需要可变参数?先看痛点场景
日常编程中,你可能遇到过这样的问题:
- 想写一个“求和函数”,但不确定用户会传入2个、3个还是更多数字;
- 想封装一个通用接口调用函数,不同接口的参数名和数量都不一样;
- 想扩展已有函数,不修改原有参数列表的前提下,新增可选项。
如果用普通参数写法,要么需要定义大量默认参数(繁琐且有限),要么直接无法满足需求。而*args和**kwargs正是为解决“参数数量/名称不确定”而生,让函数变得更灵活、更通用。
二、*args:接收任意数量的“位置参数”
1. 基础定义
*args 是 “variable-length argument list”(可变长度参数列表)的缩写,核心作用是:让函数接收任意数量的位置参数,这些参数会被打包成一个元组(tuple) 供函数内部使用。
- 语法注意:
*是核心标识,args是约定俗成的变量名(可自定义,比如*nums、*values,但建议用args保持可读性)。
2. 实战案例1:通用求和函数
需求:写一个函数,能计算任意多个数字的和(1个、2个、10个都可以)。
# 用*args实现通用求和
def sum_all(*args):
print("接收的参数打包为元组:", args) # 查看args的类型和内容
total = 0
for num in args:
total += num
return total
# 测试:传入不同数量的参数
print(sum_all(1, 2)) # 传入2个参数 → 输出3
print(sum_all(3, 4, 5)) # 传入3个参数 → 输出12
print(sum_all(10, 20, 30, 40))# 传入4个参数 → 输出100
print(sum_all()) # 传入0个参数 → 输出0(元组为空)
运行结果:
接收的参数打包为元组: (1, 2)
3
接收的参数打包为元组: (3, 4, 5)
12
接收的参数打包为元组: (10, 20, 30, 40)
100
接收的参数打包为元组: ()
0
3. 实战案例2:结合固定参数使用
*args 可以和固定位置参数搭配,但要注意:固定参数必须放在*args前面(否则Python无法区分固定参数和可变参数)。
# 固定参数(name)+ 可变参数(*args):打印姓名和多个爱好
def print_hobbies(name, *args):
print(f"姓名:{name}")
print("爱好:", end="")
for hobby in args:
print(hobby, end=" ")
print("\n" + "-"*30)
# 测试
print_hobbies("小明", "篮球", "编程", "看电影")
print_hobbies("小红", "画画", "读书")
print_hobbies("小刚") # 可变参数可省略
运行结果:
姓名:小明
爱好:篮球 编程 看电影
------------------------------
姓名:小红
爱好:画画 读书
------------------------------
姓名:小刚
爱好:
------------------------------
4. 关键细节:解包元组/列表传入*args
如果已有一个元组或列表,想把其中的元素作为位置参数传给*args,可以在传入时加*进行“解包”(避免把整个元组/列表当作一个参数传入)。
def multiply(*args):
result = 1
for num in args:
result *= num
return result
# 已有一个列表,想传入函数作为多个参数
nums = [2, 3, 4]
# 错误写法:把整个列表当作一个参数,args = ([2,3,4],)
print(multiply(nums)) # 输出:[2, 3, 4](列表不能直接相乘,实际会报错,这里仅示意)
# 正确写法:用*解包列表,args = (2, 3, 4)
print(multiply(*nums)) # 输出:24(2*3*4)
# 元组同理
nums_tuple = (5, 6)
print(multiply(*nums_tuple)) # 输出:30(5*6)
三、**kwargs:接收任意数量的“关键字参数”
1. 基础定义
**kwargs 是 “keyword arguments”(关键字参数)的缩写,核心作用是:让函数接收任意数量的关键字参数(key=value形式),这些参数会被打包成一个字典(dict) 供函数内部使用。
- 语法注意:
**是核心标识,kwargs是约定俗成的变量名(可自定义,比如**params、**options,建议用kwargs)。 - 关键字参数:传入时必须指定参数名(如
name="张三"),顺序无关。
2. 实战案例1:通用配置打印函数
需求:写一个函数,接收任意多个配置项(如host="localhost"、port=8080),并格式化输出。
def print_config(**kwargs):
print("配置项(字典形式):", kwargs)
print("格式化输出:")
for key, value in kwargs.items():
print(f" {key}: {value}")
print("\n" + "-"*30)
# 测试:传入不同数量、不同名称的关键字参数
print_config(host="localhost", port=8080, debug=True)
print_config(username="admin", password="123456", timeout=30)
print_config() # 可省略关键字参数
运行结果:
配置项(字典形式): {'host': 'localhost', 'port': 8080, 'debug': True}
格式化输出:
host: localhost
port: 8080
debug: True
------------------------------
配置项(字典形式): {'username': 'admin', 'password': '123456', 'timeout': 30}
格式化输出:
username: admin
password: 123456
timeout: 30
------------------------------
3. 实战案例2:结合固定参数和*args使用
**kwargs 可以和固定参数、*args 搭配,但参数顺序必须严格遵循:
**固定位置参数 → *args → 关键字参数(可固定)→ kwargs
# 完整参数顺序示例:固定位置参数 + *args + 固定关键字参数 + **kwargs
def user_info(name, age, *hobbies, gender="未知", **other_info):
print(f"基本信息:姓名={name},年龄={age},性别={gender}")
print(f"爱好:{hobbies if hobbies else '无'}")
print(f"其他信息:{other_info if other_info else '无'}")
print("-"*50)
# 测试
user_info("小明", 20, "篮球", "编程", gender="男", address="北京", phone="123456")
user_info("小红", 18, "画画", "读书", school="XX大学") # 省略gender和other_info
user_info("小刚", 22) # 仅传固定位置参数
运行结果:
基本信息:姓名=小明,年龄=20,性别=男
爱好:('篮球', '编程')
其他信息:{'address': '北京', 'phone': '123456'}
--------------------------------------------------
基本信息:姓名=小红,年龄=18,性别=未知
爱好:('画画', '读书')
其他信息:{'school': 'XX大学'}
--------------------------------------------------
基本信息:姓名=小刚,年龄=22,性别=未知
爱好:无
其他信息:无
--------------------------------------------------
4. 关键细节:解包字典传入**kwargs
如果已有一个字典,想把其中的键值对作为关键字参数传给**kwargs,可以在传入时加**进行“解包”。
def register(username, password, **kwargs):
print(f"注册成功:用户名={username},密码={password}")
for key, value in kwargs.items():
print(f" 额外信息:{key}={value}")
# 已有一个字典存储额外信息
extra_info = {"email": "test@xxx.com", "phone": "654321", "vip": True}
# 解包字典传入**kwargs
register("admin", "123456", **extra_info)
运行结果:
注册成功:用户名=admin,密码=123456
额外信息:email=test@xxx.com
额外信息:phone=654321
额外信息:vip=True
四、*args和**kwargs的核心区别与适用场景
| 特性 | *args | **kwargs |
|---|---|---|
| 接收参数类型 | 位置参数(无参数名) | 关键字参数(key=value) |
| 内部存储形式 | 元组(tuple) | 字典(dict) |
| 传入方式 | 直接传值(如1,2,3) | 键值对(如a=1,b=2) |
| 解包符号 | *(解包元组/列表) | **(解包字典) |
| 适用场景 | 不确定参数数量,无需参数名 | 不确定参数数量+需要参数名 |
典型使用场景:
*args:批量处理数值(求和、求积)、传递可变数量的无名称参数(如多个爱好、多个数据);**kwargs:函数配置项(如接口请求参数、类的初始化配置)、兼容多版本函数参数(新增参数无需修改原有调用)。
五、实战进阶:函数嵌套中的*args和**kwargs
*args和**kwargs在函数嵌套(比如封装第三方库函数、自定义装饰器)中非常实用,能实现“参数透传”(不修改中间函数,直接把参数传给内部函数)。
案例:封装requests.get函数
需求:封装一个HTTP请求函数,默认添加超时配置,同时支持用户传入任意请求参数(如headers、params)。
import requests
def my_get(url, *args, timeout=10, **kwargs):
"""封装requests.get,默认超时10秒,支持任意额外参数"""
try:
# 透传*args和**kwargs到requests.get
response = requests.get(url, *args, timeout=timeout, **kwargs)
response.raise_for_status() # 抛出HTTP错误
return response.text
except Exception as e:
return f"请求失败:{str(e)}"
# 测试:传入自定义headers和params(通过**kwargs透传)
headers = {"User-Agent": "Mozilla/5.0"}
params = {"key": "python", "page": 1}
result = my_get("https://httpbin.org/get", headers=headers, params=params)
print(result)
说明:这里*args和**kwargs接收了requests.get的其他参数(如headers、params),并直接透传,无需关心这些参数的具体名称和数量,极大提升了函数的通用性。

浙公网安备 33010602011771号