Python函数参数高级教程_默认参数与可变参数

Python函数参数设计需理解“参数何时被创建”“值如何传递”“对象是否可变”三大逻辑:默认参数在定义时求值,可变对象作默认值易出错,应以None代替并在函数内初始化;args收集剩余位置参数为元组;kwargs收集剩余关键字参数为字典;参数顺序应为普通参数→args→默认参数→kwargs,兼顾灵活性与可读性。

Python函数的参数设计灵活而强大,默认参数和可变参数是日常开发中最常用也最容易出错的两个特性。掌握它们的关键,不在于死记语法,而在于理解“参数何时被创建”“值如何被传递”“对象是否可变”这三个底层逻辑。

默认参数:只在定义时计算一次

默认参数的值在函数定义时就被求值并绑定,而不是每次调用时重新生成。这对不可变对象(如数字、字符串、None)影响不大,但对可变对象(如列表、字典)可能引发意外行为。

常见陷阱示例:

def add_item(item, lst=[]):  # 危险!默认列表在定义时创建
    lst.append(item)
    return lst

print(add_item(1)) # [1] print(add_item(2)) # [1, 2] ← 意外!复用了上次的列表

正确写法是用 None 作占位符,在函数体内初始化:

立即学习“Python免费学习笔记(深入)”;

  • 始终用 None 作为可变默认参数的占位值
  • 在函数开头显式判断并创建新对象:if lst is None: lst = []
  • 避免在默认值中调用会改变状态的函数(如 datetime.now()

*args:收集位置参数为元组

*args 把所有未匹配的位置参数打包成一个 tuple,它必须放在普通参数之后、**kwargs 之前。

典型用途:

  • 封装或转发参数:比如写一个日志装饰器,需要原样传入被装饰函数的所有参数
  • 实现类似内置函数的接口:如 sum(*numbers) 或自定义的 max_by_key(key_func, *items)
  • 与解包配合使用:func(*[1,2,3]) 等价于 func(1,2,3)

注意:*args 不捕获关键字参数,也不会影响参数名匹配;它只是“剩下的位置参数”。如果调用时混用位置和关键字,已命名的参数仍需满足签名要求。

**kwargs:收集关键字参数为字典

**kwargs 将所有未被显式声明的关键字参数收集成一个 dict。它必须是参数列表中的最后一个。

实用技巧:

  • 用于配置类或函数的“灵活选项”:def create_plot(data, **plot_opts): plt.plot(data, **plot_opts)
  • 安全地传递子组件参数:父函数只处理自己关心的参数,其余通过 **kwargs 透传
  • 检查非法关键字:allowed = {'color', 'linewidth', 'alpha'}; invalid = set(kwargs) - allowed

提醒:不要在函数内部直接修改 kwargs 字典(如 kwargs.pop('debug')),除非你明确知道后续代码不再依赖它;更推荐用 kwargs.get('debug', False) 安全读取。

组合使用:顺序与可读性优先

合法的完整参数顺序是:普通参数 → *args → 默认参数 → **kwargs(Python 3.8+ 支持仅限位置参数 /,但此处不展开)。

例如:

def send_request(url, *args, timeout=30, verify=True, **headers):
    ...

这样调用都合法:

  • send_request("https://api.com")
  • send_request("https://api.com", "POST", json={"x":1})("POST"进 args,json 进 headers)
  • send_request("https://api.com", timeout=5, Authorization="Bearer...")

关键原则:参数顺序要让人一眼看出哪些是必填、哪些是扩展、哪些是配置;避免过度堆砌 *args/**kwargs,必要时拆分成多个明确命名的参数提升可维护性。