新手必看:默认参数常见陷阱
在 PyCharm 中,新手利用 Python 函数的默认参数写代码时,常常会掉进Python函数默认参数陷阱。即使代码没有出现Traceback (most recent call last):等红色报错字样,运行结果却与预期不符,这正是Python函数默认参数错的典型表现。更令人头疼的是,即使新手将自己的需求详细描述给 AI 并生成代码,也常常无法避免掉入这个新手常踩的坑。
Python 新手遇到这种情况,往往不知道怎么修改,怎么改都不对,感到十分焦虑和烦恼。这个错误就像一个坚固的笼子,困住了 Python 新手,让人使尽力气也挣脱不出来。我对此深表同情。这种没有 PyCharm 报错提示的错误确实非常隐蔽,很难定位问题代码所在。
其实,我刚开始写代码时,也犯过这个Python函数默认参数错。当时我写了一个添加商品列表的函数,默认参数设置为一个空列表[]。令我没想到的是,第二次调用该函数添加“西瓜”后,打印出来的商品列表竟然包含了两个值:一个是第一次调用该函数时添加的值“奶粉”,另一个是第二次调用该函数时添加的值“西瓜”!
这把我彻底搞懵了。为什么代码没有报错,运行结果却出现异常呢?我搜索了半天,终于发现,原来是Python函数的可变默认参数在搞鬼!
那么,怎么解决这个Python函数默认参数常见陷阱呢? 别担心,本章就教新手1招根治这个问题的办法,达到治本的效果。不过在介绍这一招解决Python函数默认参数常见陷阱的方法之前,咱们得先快速回顾一下 Python 函数中位置参数、关键字参数和默认参数的基础语法知识。
快速理解:位置参数、关键字参数、默认参数
Python函数的位置参数
在调用Python函数时,要按照定义函数时形参的顺序来传递实参。
注意实参的位置一定要和形参的位置一一对应。否则,如果调用时传入的实参数量多于或少于定义函数时形参的数量,就会报TypeError。如果只是实参的位置没有和形参的位置一一对应,比如位置颠倒了,虽然不会报TypeError,但运行结果会异常,出乎新手的意料,达不到预想的效果。
其代码案例如下。
deforder_meal(name,food):print(f"{name}要定一份{food}。")order_meal("刘明","肯德基")#调用函数时按照定义Python函数时的形参的顺序传递实参order_meal("肯德基","刘明")""" 不按照定义Python函数时的形参的顺序传递实参,不报错,只不过运行结果不能满足你预想的那样 """#order_meal("肯德基")#给这行代码去掉注释时,调用Python函数时,传递的实参数量少于定义函数时的形参的数量,会报TypeError#order_meal("刘红","水果捞","肯德基")#给这行代码去掉注释时,调用Python函数时,传递的实参数量多于定义函数时的形参的数量,会报TypeError其运行结果如下。
Python函数的关键字参数
定义Python函数时,可以通过"形参名=值"的形式来定义形参。这样一来,调用函数时即使把实参的顺序打乱了,也不会报TypeError,对运行结果几乎没有任何影响。
新手在调用函数时,可以把关键字参数和位置参数混合使用。但要注意:位置参数必须写在前面,关键字参数必须写在后面,否则会报SyntaxError。
其代码案例如下。
deforder_meal(name,food):""" 定义函数时,name 和 food 是形参。 调用时既可以用位置参数,也可以用【关键字参数】(命名参数)的方式传入。 关键字参数的语法格式:形参名=实参值 """print(f"{name}要定一份{food}。")# 【关键字参数】通过 形参名=实参值 的方式传递实参# Python会根据"名字"将实参匹配给对应的形参,而不是根据"位置"order_meal(name="刘明",food="肯德基")# 输出:刘明要定一份肯德基。# 【顺序可以打乱】关键字参数最大的优势:不依赖定义时的顺序# 即使把 food 写在前面、name 写在后面,Python也能正确识别并赋值# 这与位置参数完全不同——位置参数顺序错了就会张冠李戴order_meal(food="肯德基",name="刘明")# 同样正确输出:刘明要定一份肯德基。# 【混用规则】关键字参数也可以和位置参数混合使用# 但位置参数必须写在前面,关键字参数写在后面order_meal("刘明",food="肯德基")# 正确:先位置参数,后关键字参数# 下面这种写法会报 SyntaxError,因为位置参数不能跟在关键字参数后面#order_meal(name="刘明", "肯德基") # 错误!# 【新手建议】当参数较多、或容易混淆顺序时,建议优先使用关键字参数# 代码可读性更强,且彻底避免了"位置颠倒导致逻辑错乱"的问题其运行结果如下。
Python函数的默认参数
Python函数的默认参数,就是给形参预先设定一个默认值。这样调用函数时,即使不传入与该形参对应的实参,函数也能正常调用。
不过新手要特别注意:定义函数时,默认参数一定要放在非默认参数(包括位置参数和关键字参数)的后面,否则会报SyntaxError。比如下面这段代码就是错误的。
# 【重要语法规则】默认参数必须放在非默认参数(位置参数)的后面# 下面这种定义方式是错误的:deforder_meal(drink="牛奶",name,food):# SyntaxError!print(f"{name}要定一份{food}和一瓶{drink}。")order_meal(drink="茶","刘明","水果沙拉")其运行结果如下图所示。
正确使用Python函数默认参数的代码如下所示。
deforder_meal(name,food,drink="牛奶"):""" 定义函数时,name 和 food 是【位置参数】(必填), drink="牛奶" 是【默认参数】——在形参后面直接赋予默认值。 默认参数的特点:调用时可以省略,省略就自动使用预设的默认值。 """print(f"{name}要定一份{food}和一瓶{drink}。")# 【使用默认值】只传前两个必填参数,drink 使用默认值 "牛奶"# 因为 drink 有默认值,所以调用时可以不传order_meal("刘明","水果捞")# 输出:刘明要定一份水果捞和一瓶牛奶。# 【覆盖默认值】传入第三个实参,用 "果汁" 覆盖默认的 "牛奶"# 默认参数不是固定的,调用时传入实参就会覆盖它order_meal("刘明","水果捞","果汁")# 输出:刘明要定一份水果捞和一瓶果汁。# 【用关键字参数覆盖默认值】也可以跳过位置,直接通过名字指定# 这样即使参数很多,也能精准地只改某一个默认参数order_meal("刘明","水果捞",drink="可乐")# 输出:刘明要定一份水果捞和一瓶可乐。其运行结果如下图所示。
写代码前必须注意的关键点
新手在写代码前,一定要先学会安装和使用PyCharm和Python.exe。因为如果不会的话,以后写代码的步骤就无法进行,就像必须先有水,鱼才能在里面生存一样。
如果新手还不会安装和使用PyCharm和Python.exe,请参考如下两篇文章。
安装Pycharm+配置python.exe:0基础新手一次成功
如何使用PyCharm?0基础新手必看!七大核心功能+越用越有意思
一招根治默认参数常见陷阱
新手在用 Python 函数的默认参数写代码时,不知不觉就掉进了这个常见陷阱。比如下面这段代码。
defbuy_stationery(stationery,stationery_list=[]):# ⚠️ 陷阱:默认参数在函数定义时只计算一次,这个 [] 被所有调用共享。# 第一次调用后列表里已有内容,第二次调用会继续追加,而不是新建空列表。stationery_list.append(stationery)returnstationery_list# 第一次调用:返回 ['铅笔']print(f"刘红买的文具有:{buy_stationery('铅笔')}。")# 第二次调用:返回 ['铅笔', '钢笔'] ← 意外!新手预期是 ['钢笔']print(f"刘红买的文具有:{buy_stationery('钢笔')}。")其运行结果如下图所示。
这是怎么掉进去的呢?
原来,新手在定义函数时把默认参数设成了空列表 [],结果这个列表被所有调用共享了。也就是说,每次调用函数却不传这个参数时,用的都是之前调用时已经修改过的那个列表,而不是重新创建一个空列表。
这该怎么办呢?
没事,不要慌,这里有一招就能避开 Python 函数默认参数陷阱。
只要在定义函数时,把默认参数从 [] 改成 None 就行。因为 None 是不可变的,不存在被多个调用共享的问题,每次调用都会各自创建独立的列表,互不干涉,互不影响。然后在函数内部加一个 if 条件判断:如果参数满足 is None,就新建一个空列表。如果新手对 Python 的 if 语法还不掌握,可以参考下面这篇文章。
Python新手在PyCharm写if总报错?5个坑90%人踩过,看完修复
比如如下代码。
defbuy_stationery(stationery,stationery_list=None):# ✅ 修正:用 None 做默认值,而不是 []。# None 是不可变的,不会"累积"数据,所以每次调用都是安全的。ifstationery_listisNone:# 只有调用者没传参数时,才在这里新建一个独立的空列表。# 这样每次调用都会得到自己专属的新列表,不会跟别人共享。stationery_list=[]stationery_list.append(stationery)returnstationery_list# 第一次调用:新建空列表 → 放入 '铅笔' → 返回 ['铅笔']print(f"刘红买的文具有:{buy_stationery('铅笔')}。")# 第二次调用:再次新建空列表 → 放入 '钢笔' → 返回 ['钢笔']# 两次互不影响,符合新手直觉。print(f"刘红买的文具有:{buy_stationery('钢笔')}。")其运行结果如下图所示。
复盘总结:你收获到了什么,如何根治默认参数常见陷阱
新手看到这里,相信你们已经学到了Python函数的位置参数、关键字参数、默认参数,相信你们已经掌握了Pycharm和Python.exe的安装与使用,相信你们已经了解了常见的Python函数默认参数陷阱及其一招根治方法。相信你们已经收获满满,并且已经超过了大部分遇到Python函数默认参数陷阱时还在胡乱修改的编程者。你们已经很了不起了!
专栏下篇剧透
在本专栏的下一篇中,我将分享如何根治利用Python函数语法知识写代码时的痛点,同时系统性地过一遍Python函数语法知识。
如果新手朋友们觉得我写的文章有帮助、有用,或者喜欢读我写的文章,那么请动一动你们的金手指来点赞、收藏、转发、关注。
关注我,我会为大家发布更多PyCharm及其他编程语言的实战内容、Python及其他编程语言的基础语法痛点突破、Python及其他编程语言的语法知识,以及编程软件的安装和使用干货。
如果新手朋友们觉得我写的文章有问题或有任何想法,那么欢迎到评论区留言交流。