Javthon古法: Python中哪些让人搞不清的参数

现代 Python 的参数系统远不止"传参调用"这么简单------仅位置形参、仅关键字形参、*args**kwargs 的拆包魔法、默认值的绑定时机......这些特性相互交织,让很多人学完后依然一头雾水。本文从实参和形参两个视角,系统拆解 Python 函数参数的传递规则、组合约束与底层绑定逻辑,帮你彻底告别"参数混乱",写出更清晰、更安全的函数签名。

Python 官方文档

参数概念

术语 英文 含义 出现的侧
实参 argument 函数调用时实际传入的值 调用侧
形参 parameter 函数定义时声明的变量名 定义侧
python 复制代码
def func(a, b, c):      # a, b, c 是 parameter(形参)
    ...

func(1, 2, 3)           # 1, 2, 3 是 argument(实参)

传递实参(argument)

  1. 位置实参
  2. 关键字实参
  3. [可迭代 Iterable 对象拆包](#可迭代 Iterable 对象拆包 "#%E5%8F%AF%E8%BF%AD%E4%BB%A3-iterable-%E5%AF%B9%E8%B1%A1%E6%8B%86%E5%8C%85")
  4. 字典拆包

位置实参

ℹ️ 信息

把实际参数传递给函数最常见的方式。

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")  

func(1, 2, 3)  

关键字实参

💡 提示

使用关键字参数时,实际参数的顺序并不需要与函数定义中形式参数的顺序匹配。

所以我们可以不用记住函数定义中形式参数的顺序。

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")  

func(c=3, b=2, a=1)  
位置实参与关键字实参的混用顺序

⚠️ 警告

位置参数必须出现在所有关键字参数的前面。

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")  

func(c=3, b=2, 1)
"""报错
    func(c=3, b=2, 1)  # ❌ SyntaxError: positional argument follows keyword argument
                    ^
SyntaxError: positional argument follows keyword argument
"""

正确版本:

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")  

func(1, c=3, b=2)

可迭代 Iterable 对象拆包

📝 笔记

把一个可迭代对象的元素作为位置参数传递给一个函数。

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")

values = ("深圳", "图书馆", "北馆")  
func(*values)

💡 提示

这里的 * 相当于是一种展开语法。

  • 函数调用中的 * 是"展开运算符",主动把一个可迭代对象打散成多个实参(使用 * 时,Python 调用的是可迭代对象的迭代器协议
  • 赋值中的 * 是"贪婪捕获符",标记一个变量去收集未被其他变量匹配的元素
python 复制代码
values = ("深圳", "图书馆", "北馆")  

# 贪婪捕获  
a, *rest = values  
print(f"{a=} {rest=}")   # a='深圳' rest=['图书馆', '北馆']

# 展开运算符  
print(['我', '爱', *values]) # ['我', '爱', '深圳', '图书馆', '北馆']

❌ 错误

可迭代对象的元素个数与函数形参的个数必须一样,否则会报错。

python 复制代码
# TypeError: func() missing 1 required positional argument: 'c'
values = ("深圳", "图书馆")  
func(*values)

# TypeError: func() takes 3 positional arguments but 4 were given
values = ("深圳", "图书馆", "北馆", "学习Python")  
func(*values)

字典拆包

📝 笔记

** 拆包操作符在函数调用中专门用于映射类型 。它将字典的键值对拆解为关键字实参要求字典的键必须是字符串且与形参名匹配

字典的键 "a""b""c" 变成了关键字参数名,对应的值变成了实参值,恰好与形参名 abc 一一匹配,所以输出 a='深圳' b='图书馆' c='北馆'

python 复制代码
def func(a, b, c):  
    print(f"{a=} {b=} {c=}")

values = {  
    "c": "北馆",  
    "b": "图书馆",  
    "a": "深圳",  
}  
# 等价于 func(a="深圳", b="图书馆", c="北馆")  
func(**values)

实参混合 ⭐

📝 核心理解

  1. 位置参数(这里不包括 *expr)必须出现在所有关键字参数(包括 **expr)的前面
  2. 延迟占位

实参必须按照以下顺序进行传递:

  1. 首先是位置参数:包括普通的位置参数和可迭代对象拆包
  2. 接着是关键字参数,它可以与可迭代对象混合
  3. 最后是字典拆包:它可以与关键字参数混合
python 复制代码
def func(a, b, c, d, e, f):  
    print(f"{a=} {b=} {c=} {d=} {e=} {f=}")  

# 普通位置参数与可迭代对象拆包混合  
func(*(1, 2), 3, 5, *[4], **{'f': 6})  

# 关键字参数与可迭代对象拆包混合  
func(f=6, *[1, 2, 3, 4, 5])  
func(1, e=4, *(2, 3), *(5,), **{'f': 6})  
func(*(1, 2), e=5, *[3, 4], f=6)

# 字典拆包与关键字参数混合  
func(1, **{'c': 3, 'e': 5}, d=3, **{'f': 6}, b=2, )  
func(1, **{'b': 2, 'c': 3}, d=4, **{'e': 5, 'f': 6})  

📋 摘要:延迟占位(Delayed Slot Occupation)

Python 在函数调用时,参数绑定遵循分阶段执行 的顺序,导致某些"写在后面"的实参反而先占坑

绑定顺序(不可改变)

  1. 位置实参先跑

    所有裸露位置实参 + 所有 *expr 展开的元素,按出现顺序拼成一条队列,依次占据形参槽位

  2. 关键字实参后跑

    所有裸露关键字实参 + **expr 展开的键值对,在位置实参全部就位后,再去匹配剩余空槽

核心矛盾

bash 复制代码
书写顺序  ≠  绑定顺序
关键字在前 ≠ 关键字先占坑
*expr 在后 ≠ *expr 后占坑

即使你在调用时把 d=4 写在 *(5,) 前面,*(5,) 展开的 5 也会先于 d=4 去占据形参 d 的槽位,导致关键字实参后处理时发现冲突。

python 复制代码
def func(a, b, c, d, e, f):
    print(f"{a=} {b=} {c=} {d=} {e=} {f=}")

# ❌ TypeError: func() got multiple values for argument 'd'
func(1, *(2, 3), d=4, *(5,), **{'f': 6})

安全原则

  • *expr 放在所有关键字实参之前 → 不会产生延迟占位
  • *expr 放在关键字实参之后 → 需确保展开的元素数量不会越界到已声明关键字的形参位置

定义形参(parameter)

  1. 位置和关键字形参(默认形参)
  2. 可变数量的位置形参
  3. 可变数量的关键字形参

位置和关键字形参(默认形参)

ℹ️ 信息

  1. 使用方实参角度:同时允许位置实参和关键字实参
  2. 定义方形参角度:分为必须形参(无默认值)和可选形参(有默认值)又叫关键字参数,且必须形参必须排在可选形参之前

必须的形参与可选的形参差别:

python 复制代码
def func_2(a=None):  
    """a 是可选的------调用时可以不传"""  
    if a is not None:  
        """do something"""  
        ...

def func_3(a):  
    """a 是必传的------调用时必须提供"""  
    pass  

func_2()  
func_3(2)
默认形参
python 复制代码
def func(a, b=2, c=3):  
    print(f"{a=} {b=} {c=}")  

func(1)

⚠️ 警告:Python 硬性语法规则

有默认值的形参(可选形参)必须出现在所有无默认值的形参(必须形参)之后。

python 复制代码
# ❌ 语法错误:可选形参在前,必须形参在后
def func(b=2, a, c=3):
    ...              # ↑ a 没有默认值,但夹在可选形参中间
# SyntaxError: parameter without a default follows parameter with a default

这条规则的核心原因在于位置实参的绑定方式------位置实参是按顺序"填坑"的:

python 复制代码
def func(b=2, c=3, a):   # 假设这是合法的
    ...

# 调用时只传一个位置实参:
func(1)
# 问题:这个 1 应该给 b,还是跳过 b、c 直接给 a?
#
# 如果 1 给 b → a 没有值 → 报错
# 如果 1 跳过去给 a → b 和 c 已经是可选的了,Python 没法判断你的意图

Python 没法判断你的意图

可变的默认值

🚨 危险:可变默认值陷阱

默认值在函数定义时 只计算一次,之后每次调用复用同一个对象 。 当默认值为列表、字典或类实例等可变对象时,多次调用会累积副作用

python 复制代码
def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

执行结果:

截图描述:三次调用分别输出 [1][1, 2][1, 2, 3],说明每次调用都在修改同一个列表对象。

可变数量的位置形参

📝 笔记

*args 的核心语义是:贪婪吸收所有剩余的位置实参
截图描述:示意图展示 *args 将形参列表"切割"为前后两部分,*args 之前的位置形参按顺序绑定传入的位置实参,*args 贪婪吸收所有剩余位置实参,*args 之后的形参自动变为仅关键字形参。

python 复制代码
def concat(city="深圳", *args, sep="/"):  
    print(f"{type(args) = } {args=}")  
    return sep.join((city,) + args)  

print(concat("北京", "上海", "广州", sep=" | "))  
print(concat())

输出:

python 复制代码
type(args) = <class 'tuple'> args=('上海', '广州')
北京 | 上海 | 广州
type(args) = <class 'tuple'> args=()
深圳

可变数量的关键字形参

📝 笔记

**kwargs 的核心语义是:贪婪吸收所有未匹配的关键字实参,打包成一个字典。

*args 贪婪吸收未匹配的位置实参,**kwargs 贪婪吸收所有未匹配的关键字实参,然后无透明传递query_data 函数。

python 复制代码
def query_data(table, limit=0, offset=20):  
    print(f"select from {table} limit {limit, offset}")

def wrapper(*args, **kwargs):  
    print(f"{args=} {kwargs=}")  
    query_data(*args, **kwargs)  # 无透明传递  
    print("-"*45)  

wrapper("user", limit=10, offset=20)  
wrapper("user", 10, 20)  
wrapper(**{"table": "user", "limit": 10, "offset": 20})

输出:

sql 复制代码
args=('user',) kwargs={'limit': 10, 'offset': 20}
select from user limit (10, 20)
---------------------------------------------
args=('user', 10, 20) kwargs={}
select from user limit (10, 20)
---------------------------------------------
args=() kwargs={'table': 'user', 'limit': 10, 'offset': 20}
select from user limit (10, 20)
---------------------------------------------

仅位置形参

📝 笔记

/ 分隔符强制其左边的形参只能通过位置传递,不能用关键字指定/ 左边的参数剥夺了调用者使用关键字传参的权利

python 复制代码
def func(pos1, pos2, /, pos_or_kw1, pos_or_kw2, *, kw1, kw2):
    """
    pos1, pos2          → 仅位置(/ 左边)
    pos_or_kw1, pos_or_kw2 → 位置或关键字(/ 和 * 之间)
    kw1, kw2            → 仅关键字(* 右边)
    """
    pass

# 合法调用
func(1, 2, 3, 4, kw1=5, kw2=6)        # ✅
func(1, 2, 3, pos_or_kw2=4, kw1=5, kw2=6)  # ✅

# 非法调用
func(pos1=1, pos2=2, ...)  # ❌ TypeError: got positional-only arguments as keyword
python 复制代码
print(func.__code__.co_posonlyargcount)  # 2  
print(func.__code__.co_kwonlyargcount)   # 2  
print(func.__code__.co_varnames)         # ('pos1', 'pos2', 'pos_or_kw1', 'pos_or_kw2', 'kw1', 'kw2')

截图描述:流程图展示了仅位置形参的参数绑定检查逻辑。

⚠️ 警告

注意,仅位置形参也是可选的。

python 复制代码
def func(a, b=2, /):  
    print(f"{a=} {b=}")  

func(1)  
func(1, 5)

🚨 危险

但是还是遵守必须形参在前,可选形参在后

python 复制代码
# ✅ 合法:必须形参在前,可选形参在后
def func(a, b, /, c, d=4):
    pass

# ❌ 非法:可选形参在前,必须形参在后(即使跨 / 也不允许)
def func(a, b=2, /, c):  
    pass
# SyntaxError: parameter without a default follows parameter with a default

📝 笔记

参数名可能在未来改变。库作者想保留重命名参数的权利,而不破坏用户代码。

python 复制代码
# 需求:库函数要求用户"按顺序提供,不要依赖参数名"
def configure_connection(
    host: str,
    port: int,
    timeout: float,
    /,                    # ← 这些参数名是内部细节
    *,
    pool_size: int = 10,  # ← 这些参数名是公开 API
    retry_attempts: int = 3,
) -> ConnectionPool:
    """
    建立连接池。
    
    host, port, timeout 按位置提供------顺序即语义。
    pool_size, retry_attempts 按关键字提供------名称即文档。
    """
    ...

# 使用:自文档化且安全
pool = configure_connection("db.internal", 5432, 5.0, pool_size=20)
同名的关键字实参

📝 笔记

仅位置形参的名字不会"占用"关键字命名空间

注意下面的代码关键字传递实参 name="深圳图书馆",并没有影响前面位置参数,也不会像之前报错。

python 复制代码
def func_name(name, /, **kwargs):  
    print(f"{name=} \n{kwargs=}")  

# 注意这里关键字传递实参 name="深圳图书馆",并没有影响前面位置参数 
func_name("Pkmer", name="深圳图书馆")  
"""输出  
name='Pkmer'
kwargs={'name': '深圳图书馆'}  
"""

截图描述:流程图展示了仅位置形参与关键字实参的绑定流程。

仅关键字形参

📝 笔记

两种方式:

  1. 可变数量的位置形参的后面
  2. 单独的 * 后面
python 复制代码
def kwonly1(*args, c):  
    print(f"{args=} {c=}")  

kwonly1(1, 2, c=3)  
kwonly1(c=3)  
"""输出  
args=(1, 2) c=3  
args=() c=3  
"""
python 复制代码
def kwonly2(a, b=42, *, c=3):  
    print(f"{a=} {b=} {c=}")  

kwonly2(1, 2, c=3)  
kwonly2(1, c=3)  
"""输出  
a=1 b=2 c=3  
a=1 b=42 c=3  
"""

形参的组合规则

❗ 重要

  1. 仅位置形参首先出现,然后是一个 /
  2. 常规的位置形参出现在仅位置形参的后面
  3. 可变数量的位置形参出现在常规的位置形参的后面
  4. 仅关键字形参出现在可变数量的位置形参的后面
  5. 可变数量的关键字形参总是出现在最后
  6. 对于仅位置形参和常规的位置形参,所有必须的形参都是在所有默认形参的前面。
python 复制代码
def all_params(a, /, b, c=28, *args, d=256, e, **kwargs):  
    print(f"{a=} {b=} {c=} {args=} {d=} {e=} {kwargs=}")  
  
  
all_params(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, e=11, f=12, g=13)  
"""输出  
a=1 b=2 c=3 args=(4, 5, 6, 7, 8, 9, 10) d=256 e=11 kwargs={'f': 12, 'g': 13}  
"""
相关推荐
Jetev1 小时前
如何利用SQL子查询进行非结构化数据处理_文本匹配
jvm·数据库·python
老纪1 小时前
c++怎么在Windows下设置文件的安全访问控制列表(ACL)权限【底层】
jvm·数据库·python
wyhua20081 小时前
Installing the classic Jupyter Notebook interface
python
yexuhgu1 小时前
Redis怎样节省海量状态存储内存_利用Bitmap结构替代传统String存储
jvm·数据库·python
极光代码工作室1 小时前
基于大数据的交通流量分析系统
大数据·hadoop·python·数据分析·数据可视化
2301_779622411 小时前
如何修复SQL嵌套查询死锁_调整锁粒度与执行顺序
jvm·数据库·python
iAm_Ike1 小时前
HTML怎么显示灵感便签关联项目_HTML拖拽绑定项目入口【详解】
jvm·数据库·python
有梦想的小何1 小时前
Cursor AI 编程实战(篇三):Domain、Infrastructure 与策略模式
java·ai编程·策略模式
2301_809204701 小时前
SQL如何实现实时数据的滑动窗口分析_SQL性能调优
jvm·数据库·python