飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

可变参数

有时函数需要接收数量不确定的参数:处理任意多个数字的求和、接收任意多个配置项的日志函数等。Python 通过 *args 和 **kwargs 提供优雅的可变参数机制,同时支持 / 和 * 来限制参数的传递方式。

*args:接收多余的位置参数

在形参名前加 * 会把多余的位置参数收集为一个元组:

def total(*numbers):
    result = 0
    for n in numbers:
        result += n
    return result

print(total())           # 0
print(total(1, 2, 3))     # 6
print(total(1, 2, 3, 4, 5))  # 15

*args 是约定名称,可以改,但强烈建议保留。args 在函数内部是一个普通元组,支持索引、迭代、切片等所有元组操作:

def first_and_rest(first, *rest):
    return first, rest

print(first_and_rest(10, 20, 30))  # (10, (20, 30))

*args 前面可以有普通位置参数,这些参数按顺序优先匹配,剩余的才进入 *args:

def tag_list(tag, *items):
    return [f"[{tag}] {item}" for item in items]

print(tag_list("INFO", "启动", "连接成功", "就绪"))
# ['[INFO] 启动', '[INFO] 连接成功', '[INFO] 就绪']

**kwargs:接收多余的关键字参数

在形参名前加 ** 会把多余的关键字参数收集为一个字典:

def build_profile(name, **kwargs):
    profile = {"name": name}
    profile.update(kwargs)
    return profile

print(build_profile("Alice", age=30, city="Beijing"))
# {'name': 'Alice', 'age': 30, 'city': 'Beijing'}

**kwargs 同样是一个约定名称。内部字典的键是字符串(参数名),值是传入的对象。关键字参数的顺序在 Python 3.7+ 中得以保留。

*args 与 **kwargs 的组合

两者可以一起使用,*args 必须在 **kwargs 之前:

def log_event(level, *args, **kwargs):
    msg = " ".join(str(a) for a in args)
    extras = ", ".join(f"{k}={v}" for k, v in kwargs.items())
    if extras:
        msg += f" ({extras})"
    return f"[{level}] {msg}"

print(log_event("ERROR", "连接失败", code=500, retry=3))
# [ERROR] 连接失败 (code=500, retry=3)

*args 后的仅限关键字参数

*args 后面的普通参数只能通过关键字传递,不能通过位置传递:

def concat(*args, sep=" "):
    return sep.join(str(a) for a in args)

print(concat("a", "b", "c"))           # a b c
print(concat("a", "b", "c", sep="-"))  # a-b-c
# concat("a", "b", "c", "-")           # 非法!sep 必须关键字传参
# TypeError: concat() takes from 0 to 3 positional arguments but 4 were given

这个特性非常有用:它确保调用者必须显式指定某些参数的含义,避免位置参数的歧义。

仅位置参数(Python 3.8+)

在参数列表中用 / 分隔,/ 之前的参数只能按位置传递,不能用关键字:

def pow(base, exp, /):
    return base ** exp

print(pow(2, 3))      # 8
# pow(base=2, exp=3)  # TypeError: pow() got some positional-only arguments passed as keyword arguments

仅位置参数适用于以下场景:参数名没有语义价值(如数学函数)、需要强制调用者遵守参数顺序、或者参数名可能变化(API 兼容性)。

仅限关键字参数(Python 3.8+)

在参数列表中用 * 分隔,* 之后的参数必须按关键字传递:

def draw_point(x, y, *, color="black", size=1):
    return f"点({x}, {y}) 颜色={color} 大小={size}"

print(draw_point(10, 20, color="red", size=2))  # 合法
# draw_point(10, 20, "red", 2)                     # 非法!color 和 size 必须关键字传参

三种参数方式的组合

一个函数可以同时使用仅位置、位置或关键字、仅限关键字参数:

def complex_func(pos_only, /, pos_or_kwd, *, kwd_only):
    return (pos_only, pos_or_kwd, kwd_only)

print(complex_func(1, 2, kwd_only=3))        # 合法
print(complex_func(1, pos_or_kwd=2, kwd_only=3))  # 合法
# complex_func(pos_only=1, 2, kwd_only=3)   # 非法!pos_only 只能位置
# complex_func(1, 2, 3)                       # 非法!kwd_only 只能关键字

常见错误

*args 和 **kwargs 顺序写反:

# def bad(**kwargs, *args):   # SyntaxError: invalid syntax
#     pass

试图把 *args 当作列表修改(它是元组,不可变):

def broken(*args):
    args.append(1)   # AttributeError: 'tuple' object has no attribute 'append'

在 **kwargs 中使用非字符串键(调用时参数名自动转为字符串,但手动构造字典解包时可能出错):

def f(**kwargs):
    print(kwargs)

# f(**{1: "a"})     # TypeError: keywords must be strings

小结

*args 收集多余位置参数为元组,**kwargs 收集多余关键字参数为字典。*args 后的参数必须关键字传递,/ 前的参数必须位置传递,* 后的参数必须关键字传递。合理组合这些机制,可以设计出既灵活又清晰的函数接口。

上一页
默认参数
下一页
解包实参