CC++ & Algorithm

Python默认参数与关键字参数

困难3
语言版本:C++Python
概述:本文通过生活比喻讲解Python中默认参数和关键字参数的概念及用法,帮助同学们轻松理解函数的灵活调用方式。

让函数更灵活:Python默认参数与关键字参数详解

在写程序时,我们经常需要让函数能处理多种情况,但又不想每次调用都写一大堆参数。Python提供了两种非常实用的参数机制——默认参数关键字参数,它们就像点餐时的“默认套餐”和“点名点菜”,能让你的函数调用更简单、更清晰。

默认参数允许你在定义函数时给某个参数设定一个默认值,调用时如果不传这个参数,就自动使用默认值。关键字参数则允许你在调用函数时通过 参数名=值 的形式明确指定参数,这样可以不按顺序传参,代码更易读。两者常常搭配使用,让函数既灵活又安全。


为什么需要默认参数?

想象你去快餐店点餐。服务员问:“您要什么饮料?”如果你没特别说,店员会默认给你一杯可乐。但如果你喊一声“我要橙汁”,那就可以换成橙汁。这里的“可乐”就是默认选项,而“橙汁”是你特意指定的。

在Python函数里,我们也可以给参数设定一个“默认值”。这样,调用函数时如果没给这个参数,就用默认值;如果给了,就用新值。

def order_food(drink="可乐"):
    print("你的套餐搭配" + drink)

order_food()              # 输出:你的套餐搭配可乐
order_food("橙汁")        # 输出:你的套餐搭配橙汁

生活中的更多例子:

  • 计算每周零花钱:家长每周给你固定零花钱,但如果你表现好可以额外奖励。
def weekly_pocket_money(base=20, bonus=0):
    # base: 基础零花钱(默认20元), bonus: 额外奖励
    total = base + bonus
    print("本周零花钱共", total, "元")

weekly_pocket_money()               # 输出:本周零花钱共 20 元
weekly_pocket_money(bonus=5)        # 输出:本周零花钱共 25 元
  • 老师布置作业:默认写两页,但可以指定更多。
def assign_homework(pages=2):
    print("今天作业写", pages, "页")

assign_homework()         # 默认2页
assign_homework(4)        # 今天作业写4页

使用默认参数可以避免重复写相同值,让函数调用更简洁,也方便后续修改默认值。


关键字参数是什么?

还是点餐的例子。服务员问:“您的主食和饮料分别要什么?”你可以说“我要汉堡,配雪碧”。但如果你记不清顺序,干脆直接说:“饮料要雪碧,主食要汉堡”。这种直接说出参数名字的方式,就是关键字参数。

在Python中,调用函数时用 参数名=值 的形式,就是关键字参数。它让我们不按顺序传参,代码更清晰。

def make_meal(main_food, drink):
    print("主食:" + main_food + ",饮料:" + drink)

# 普通顺序调用
make_meal("汉堡", "雪碧")

# 关键字参数,顺序可以调换
make_meal(drink="雪碧", main_food="汉堡")

为什么用关键字参数?

  1. 不用记参数顺序:尤其是参数很多时,比如计算考试成绩总分的函数有chinese, math, english,直接用math=100, chinese=90比按顺序传90,100,...更不容易搞错。
  2. 提高代码可读性:一看参数名就知道传的是什么值。
def report_score(chinese, math, english):
    total = chinese + math + english
    print("语文:", chinese, "数学:", math, "英语:", english, "总分:", total)

# 用关键字参数,一目了然
report_score(math=95, chinese=90, english=88)

注意:如果混用位置参数和关键字参数,位置参数必须放在关键字参数之前。比如report_score(90, english=88, math=95)是对的,但report_score(math=95, 90, english=88)会报错,因为位置参数90出现在关键字参数后面。


把两者结合起来

默认参数和关键字参数经常一起用。比如点餐时,主食必须选,但饮料可以默认可乐。你还可以用关键字参数只改其中一项。

def order_meal(main_food, drink="可乐", dessert=None):
    print("主食:" + main_food + ",饮料:" + drink, end="")
    if dessert:
        print(",甜品:" + dessert)
    else:
        print(",无甜品")

# 只传主食,其他用默认
order_meal("薯条")                    # 主食:薯条,饮料:可乐,无甜品

# 传主食和饮料,用关键字指定饮料
order_meal("薯条", drink="橙汁")     # 主食:薯条,饮料:橙汁,无甜品

# 传主食、饮料和甜品,全部指定
order_meal("汉堡", dessert="冰淇淋", drink="雪碧")  # 顺序打乱也没关系

更贴近生活的例子: 模拟一个考试分数计算函数,总分为100分,语文、数学、英语三科,其中数学默认权重为1,其他科权重默认为1,但可以调整。

def calculate_score(chinese, math, english, weight_math=1, weight_chinese=1, weight_english=1):
    # chinese, math, english: 各科原始分数
    # weight_*: 各科权重,默认都是1
    final = chinese * weight_chinese + math * weight_math + english * weight_english
    print("加权总分:", final)

# 正常调用,全用默认权重
calculate_score(90, 80, 85)          # 相当于 90*1 + 80*1 + 85*1 = 255

# 只提高数学权重
calculate_score(90, 80, 85, weight_math=1.5)  # 90 + 80*1.5 + 85 = 295

# 调换关键字顺序也没问题
calculate_score(90, 80, 85, weight_english=0.8, weight_math=1.2)

新手容易犯的错误

  1. 默认参数写在必须参数前面
    Python不允许“先写有默认值的参数,再写无默认值的参数”,因为这样会导致歧义。

    def wrong_example(a=1, b):   # 错误!SyntaxError
        pass
    

    正确写法:必须参数在前,默认参数在后。

    def correct_example(a, b=1): # 正确
        pass
    
  2. 默认参数使用可变对象(如列表、字典)
    默认参数只在函数定义时被创建一次,如果默认值是一个可变对象(比如空列表),多次调用可能会共用同一个对象,导致奇怪的结果。

    def add_item(item, list_item=[]):
        list_item.append(item)
        return list_item
    
    print(add_item("苹果"))  # ['苹果']
    print(add_item("香蕉"))  # ['苹果', '香蕉']  —— 不是期望的空列表
    

    解决方法是改用None,然后在函数内部创建列表:

    def add_item(item, list_item=None):
        if list_item is None:
            list_item = []
        list_item.append(item)
        return list_item
    
  3. 关键字参数拼写错误
    如果关键字参数名写错,Python会报TypeError,指出不认识该参数。

    def greet(name):
        print("你好", name)
    greet(nam="小明")  # TypeError: greet() got an unexpected keyword argument 'nam'
    
  4. 混用位置参数和关键字参数时顺序错误
    位置参数必须全部写在关键字参数之前。记住:先位置后关键字


完整可运行的代码示例

下面是一个模拟“周末游玩规划”的函数,包含必须参数(地点)、默认参数(人数默认2,交通方式默认“步行”)和关键字参数(可指定天数、预算)。通过不同调用方式感受灵活性。

def plan_trip(destination, people=2, transport="步行", *, days=1, budget=100):
    # destination: 目的地(必须)
    # people: 人数(默认2)
    # transport: 交通方式(默认步行)
    # days: 天数(默认1,通过*后只能关键字传参)
    # budget: 预算(默认100,通过*后只能关键字传参)
    total_cost = people * budget * days
    print(f"周末到{destination}玩,共{people}人,交通方式:{transport}," 
          f"游玩{days}天,预算总费用:{total_cost}元")

# 1. 只传必须参数,其他全用默认
plan_trip("动物园")
# 输出:周末到动物园玩,共2人,交通方式:步行,游玩1天,预算总费用:200元

# 2. 传必须参数+部分默认参数(位置方式)
plan_trip("游乐园", 3, "地铁")
# 输出:周末到游乐园玩,共3人,交通方式:地铁,游玩1天,预算总费用:300元

# 3. 使用关键字参数指定天数与预算
plan_trip("博物馆", transport="公交", days=2, budget=50)
# 注意:people没改,默认2人
# 输出:周末到博物馆玩,共2人,交通方式:公交,游玩2天,预算总费用:200元

# 4. 混合使用,全部指定
plan_trip("科技馆", people=4, transport="出租车", days=1, budget=80)
# 输出:周末到科技馆玩,共4人,交通方式:出租车,游玩1天,预算总费用:320元

代码中用到了 * 来强制后面的参数只能以关键字形式传递(这是Python的另一种特性,称为仅限关键字参数),可以帮我们避免位置混淆。这里展示它是为了让你看到更复杂的参数组合,暂时了解即可。


相关指引

学完默认参数和关键字参数后,你可以继续探索以下内容:

  • 可变参数*args**kwargs —— 用于接收任意数量的参数,非常灵活。
  • 参数传递规则:位置参数、默认参数、可变参数、关键字参数的顺序必须严格遵守,Python官方文档有详细说明。
  • 函数文档:写好注释和docstring,让函数的使用者一目了然。
  • 命名规范:参数名要清晰易懂,避免用单字母(除非是临时变量)。

另外,如果你在编程中遇到“参数默认值总是被修改”的诡异问题,记得检查是否用了可变对象作为默认值,用None代替并手动初始化即可。

现在,你已经掌握了给函数参数设置“默认选项”和“点名传递”的本领。快去写几个自己的小函数练练手,比如设计一个计算游戏角色生命值的函数,或者一个统计零花钱开支的函数吧!

例题精讲

1单选题

在Python函数定义中,关于默认参数的位置,以下说法正确的是?

A默认参数可以放在任何位置,没有限制
B默认参数必须放在所有非默认参数的后面
C默认参数必须放在所有参数的最前面
D默认参数和非默认参数可以任意交错排列
2单选题

调用函数时使用关键字参数,以下哪种说法是正确的?

A关键字参数必须按函数定义时的顺序传递
B关键字参数只能用于有默认值的参数
C关键字参数可以打乱顺序传递,但所有参数都必须指定参数名
D关键字参数必须放在位置参数之后
3判断题

在Python中,def func(x, lst=[]): 定义了一个函数,调用 func(1) 后再调用 func(2),第二次调用时 lst 的默认值仍然是空列表[]。

4填空题
以下代码调用函数,需要输出 3 和 8,请使用关键字参数完成调用。

def multiply(a, b):
    return a * b

result1 = multiply(1, 3)  # 输出3
result2 = multiply(___)   # 输出8
print(result1, result2)
5填空题
以下代码试图统计字符出现次数,但存在默认参数陷阱。请在划线处补全函数定义,避免该陷阱(使每次调用 count_chars 时都使用新的空列表)。

def count_chars(s, lst___):
    for ch in s:
        lst.append(ch)
    return lst

print(count_chars("abc"))  # 期望 ['a','b','c']
print(count_chars("xy"))   # 期望 ['x','y'] 而不是 ['a','b','c','x','y']