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:不定长位置参数

形参写成 *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 之后,因此只能用关键字传入:

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 只是约定名,换名字不影响机制,但团队里统一用这两个名字更省事。