在 Python 中,函数形参与实参的对应方式决定外部数据如何进入函数体。除位置实参与关键字实参外,还可为形参指定默认值,并用 *args、**kwargs 接收不定数量的实参;调用侧亦可用 *、** 对序列或字典解包传参。若默认参数使用可变对象,或关键字实参与位置实参顺序不当,可能产生与预期不符的结果。本节按传参形态说明定义与调用规则。

位置实参与关键字实参

同一组形参既可按定义顺序传入位置实参,也可按名称传入关键字实参:

def describe(name, age):
    print(f"{name} 今年 {age} 岁")

describe("小明", 20)
describe(age=18, name="小红")

位置参数

位置实参按从左到右的顺序依次绑定形参;顺序错误则绑定关系错误:

def greet(first, last):
    print(f"Hi {first} {last}")

greet("Tom", "Smith")

关键字参数

写成 形参名=值 后,不再依赖左右顺序。若与位置实参混用,位置实参须位于关键字实参之前:

greet(last="Smith", first="Tom")
# greet(last="Smith", "Tom")   # SyntaxError:位置实参不能跟在关键字实参之后

默认参数

形参指定默认值后,调用可省略该实参;显式传入时覆盖默认值。默认值在函数定义时求值一次,故可变对象作默认值会在多次调用间共享,见文末常见错误:

def greet(name, greeting="Hello"):
    print(f"{greeting}, {name}!")

greet("Tom")
greet("Tom", "Hi")

*args:不定长位置参数

形参前加单个 * 时,额外位置实参收为元组;若无额外位置实参,则为空元组:

def total(*args):
    print(args)
    return sum(args)

print(total(1, 2, 3))    # 打印 (1, 2, 3),返回 6
print(total())           # 打印 (),返回 0

**kwargs:不定长关键字参数

**kwargs 将额外关键字实参收为字典,键为参数名,值为传入对象:

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key} = {value}")

print_info(name="Tom", age=18, city="Beijing")

输出:

name = Tom
age = 18
city = Beijing

混合定义时的顺序

同一条 def 可组合多种形参。常见顺序为:普通形参、*args、仅允许关键字传入的形参(可带默认值)、**kwargs。下例中 b=10 位于 *args 之后,调用时须以关键字传入 b:

def mixed(a, *args, b=10, **kwargs):
    print(a, args, b, kwargs)

mixed(1, 2, 3, b=99, x=1, y=2)
# 1 (2, 3) 99 {'x': 1, 'y': 2}

调用时解包

若实参已存在于列表、元组或字典,可在调用处使用 * 或 ** 展开。序列按位置展开,字典按键名与形参对齐:

def add3(a, b, c):
    return a + b + c

nums = [1, 2, 3]
print(add3(*nums))

info = {"a": 1, "b": 2, "c": 3}
print(add3(**info))

常见错误

  • 默认参数为 []、{} 等可变对象时,多次调用共享同一对象;应使用 def f(x=None) 并在函数内 if x is None: x = []。
  • 关键字实参之后不能再写位置实参,否则语法错误。
  • *args / **kwargs 仅为约定名称,机制不依赖该名称,但团队内宜统一以便阅读。

本节说明了 Python 中各类实参与形参的对应关系及解包调用。下一节介绍lambda 表达式:单行匿名函数及其与 sorted、filter、map 的配合。