1. 核心概念区分:形参与实参

在 Python 中,函数定义时括号里的变量叫形参(parameter),它是“占位符”;调用函数时实际传入的值叫实参(argument),它是“真数据”。

一句话概括:形参是“座位”,实参是“坐上去的人”。

# 定义函数时,name 是形参(占位符)
def greet(name):
    print(f"Hello, {name}!")
调用函数时,"Alice" 是实参(真实数据)
greet("Alice")

关键记忆点:形参是定义时的变量名,实参是调用时的具体值。

2. 参数分类详解

2.1 位置参数(positional arguments)

按顺序一一对应传递。数量不对会报错 TypeError

def add(a, b):
    return a + b
调用时顺序必须与定义一致
print(add(3, 5))  # 3 传给了 a,5 传给了 b

关键记忆点:位置参数靠“排队顺序”匹配,少了或多了都不行。

2.2 默认参数(default arguments)

在定义时为形参赋予默认值,调用时可以省略。想象成“餐厅套餐”,默认配菜就是可乐,你也可以换成雪碧。

def order_drink(drink="可乐"):
    print(f"您的饮料是:{drink}")
order_drink()          # 输出:您的饮料是:可乐
order_drink("雪碧")    # 输出:您的饮料是:雪碧

关键记忆点:默认参数让函数调用更灵活,但默认值只在函数定义时计算一次(警惕可变对象!)。

2.3 关键字参数(keyword arguments)

调用时用 形参名=值 传递,不依赖顺序,可读性更高。

def student_info(name, age, grade):
    print(f"{name},{age}岁,{grade}年级")
关键字传参,顺序可以任意
student_info(grade="三年级", age=9, name="小明")

关键记忆点:关键字参数提高调用可读性,但必须跟在位置参数后面。

2.4 可变位置参数(*args)

用一个星号 *args 收集多余的按位置传入的实参,打包成元组。简单说,“你有多少包袱,我全给你装到一个筐里”。

def sum_all(*args):
    total = 0
    for num in args:
        total += num
    return total
print(sum_all(1, 2, 3, 4, 5))  # 输出 15

关键记忆点*args 把任意数量的位置参数收集为元组,常用于不确定参数个数时。

2.5 可变关键字参数(**kwargs)

两个星号 **kwargs 收集多余的关键字参数,打包成字典。相当于“你把姓名、电话、地址都写在纸条上,我帮你装进字典里”。

def build_profile(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")
build_profile(name="小明", age=10, hobby="编程")

关键记忆点**kwargs 把任意数量的关键字参数收集为字典。

3. 参数组合与顺序规则

当多种参数同时出现在函数定义中时,必须严格遵守以下强制书写顺序(从前往后):

  1. 位置参数(普通参数)
  2. 默认参数
  3. *args
  4. 关键字参数(不带默认值的)
  5. **kwargs

综合示例:

def example(a, b=2, *args, c=10, **kwargs):
    print(f"a={a}, b={b}, args={args}, c={c}, kwargs={kwargs}")
example(1, 3, 4, 5, c=20, d=30, e=40)
输出:a=1, b=3, args=(4, 5), c=20, kwargs={'d': 30, 'e': 40}

关键记忆点:参数顺序口诀——普通、默认、*args,后面跟个关键字、**kwargs

4. 高级用法与常见陷阱

4.1 默认参数的“可变对象”陷阱

如果默认参数是列表、字典等可变对象,每次调用都会共用同一个对象,导致数据累积。来看错误与正确对比:

错误示例:

def bad_append(item, lst=[]):
    lst.append(item)
    return lst
print(bad_append(1))  # [1]
print(bad_append(2))  # [1, 2] —— 不是期望的 [2]!

解决方案: 默认值设为 None,在函数内部创建新对象。

def good_append(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst
print(good_append(1))  # [1]
print(good_append(2))  # [2]

关键记忆点:默认参数值只在定义时计算一次,可变对象要设为 None 再内部初始化。

4.2 强制位置或关键字参数(Python 3.8+)

使用 /* 可以限定参数的传递方式。

  • / 之前的参数只能按位置传递。
  • * 之后的参数只能按关键字传递。
def personal_info(name, age, /, *, city, job):
    # name 和 age 必须按位置传,city 和 job 必须按关键字传
    print(f"{name},{age}岁,住在{city},工作是{job}")
正确的调用:
personal_info("小红", 25, city="北京", job="教师")
错误的调用(解注释会报错):
personal_info(name="小红", age=25, city="北京", job="教师")  # name 和 age 不允许关键字

关键记忆点/ 划清“位置参数区”,* 划清“关键字参数区”。

4.3 参数解包(调用时使用 * 和 **)

可以用 * 解开序列、** 解开字典,让它们成为独立的参数。

def add(a, b, c):
    return a + b + c
nums = [1, 2, 3]
print(add(*nums))        # 相当于 add(1, 2, 3)
info = {"a": 4, "b": 5, "c": 6}
print(add(**info))       # 相当于 add(a=4, b=5, c=6)

关键记忆点:定义函数时用 * 包起来,调用时用 * 拆开,完美对称。

5. 总结对比表

参数类型定义语法调用方式典型场景
位置参数def f(a, b)按顺序传值固定数量、顺序敏感的输入
默认参数def f(a=10)位置或关键字,可省略提供可选配置
关键字参数(调用时) name=value形参名=值提高可读性、可选参数多的函数
*argsdef f(*args)任意多个位置值数量不定的输入(如求和、打印)
**kwargsdef f(**kwargs)任意多个 key=value配置字典、转发参数

6. 课后思考题

题目一(参数顺序改错):

def demo(a=1, b, *args):
    pass
上述定义会报 SyntaxError,请指出错误原因并写出正确的先后顺序。

提示:带默认值的参数必须放在非默认参数之后。

题目二(可变对象陷阱):

def add_student(name, student_list=[]):
    student_list.append(name)
    return student_list
class1 = add_student("张三")
class2 = add_student("李四")
print(class2)  # 输出是什么?为什么?

请先预测输出,再用可变对象陷阱的知识解释原理。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐