一不留神就容易犯错的Python语法坑
🐍

一不留神就容易犯错的Python语法坑

Tags
Python
Published
November 28, 2025
Author
Simon Wong
Python是一门简单易用的编程语言,而且目前热度非常高,尤其对玩AI来说,可以说是首选语言了。但是Python有不少语法上的坑,一不留神就会踩中,最近就踩中了一个,我给大家看看演示代码,看看你们能不能发现
def has_fruit(fruit: str) -> bool: if fruit in [ "苹果", "香蕉" "橙子", "草莓", "葡萄", "梨", "桃子", "西瓜", "橘子", ]: return True return False
当调用函数has_fruit(”橙子”)会返回True还是False?答案是False, 可能眼尖的同学会发现香蕉后面少了一个逗号,但是这个函数是没有语法错误,能够正常运行的。这里就要说到Python这个比较坑人的语法,字符串拼接。在Python中,把两个相邻的字符串字面量直接写在一起,中间不加任何符号,解释器会自动把它们拼接成一个字符串 , 这个叫隐式字符串字面量连接, 比如
a = "https://" "example.com" # 与 a = "https://example.com" 等同
这种方法还可以跨多行, 需要用括号包住, 比如我们写一个sql语句,用这种方法写起来就非常清晰
sql = ( "SELECT id, name, email " "FROM users " "WHERE register_date >= '2025-01-01' " "ORDER BY id DESC " "LIMIT 100" )
说到这里,其实还好,看着这个语法也蛮不错的,但是最坑的是它也可以在数组和集合中使用,这样如果你在数组或者集合中漏写了逗号,解释器就会默默把两边的字符串默默拼起来,变成一个字符串,导致非常隐蔽的bug, 我们回到前面的列子, 其实就变成了
def has_fruit(fruit: str) -> bool: if fruit in [ "苹果", "香蕉橙子", # 这里变成了一个字符串 "草莓", "葡萄", "梨", "桃子", "西瓜", "橘子", ]: return True return False
所以你调用has_fruit(”橙子”)返回的就是False。除了隐式字符串字面量连接,Python中还有不少非常容易产生bug的语法,今天来跟大家一起讨论讨论。
 

可变默认参数

这个可以说是Python中最著名的坑了,几乎每个新手都会犯这个错误,坑的让人怀疑人生,在Python中,默认参数在函数定义的时候就求值,而且只会求值一次,如果默认参数是可变对象(list, dict, set), 就会导致所有的函数调用都共享同一个对象,我们来看代码
def add_item(item, lst=[]): lst.append(item) return lst print(add_item(1)) # [1] print(add_item(2)) # [1, 2] ← 卧槽??? print(add_item(3)) # [1, 2, 3]
这里对于默认参数lst,再函数定义的时候就已经初始化,对于接下来的三次函数调用,lst都是同一个对像,而在调用函数时,都会给lst增加一个元素,这就导致了出现了非预期的结果。所以在给函数设置默认参数时,我们一定不要使用可变对象(list, dict, set),我们可以如下修改
def add_item(item, lst=None): lst = lst or [] lst.append(item) return lst print(add_item(1)) # [1] print(add_item(2)) # [2] print(add_item(3)) # [3]
这个错误几乎每个新手都犯过,而且带来的bug很难排查,在使用时一定要注意。
 

延迟绑定闭包

我们先来看列子
# 你想创建 0~4 每个数乘以自己的 5 个函数 funcs = [] for i in range(5): funcs.append(lambda x: x * i) # 测试一下 print(funcs[0](10)) # 你期待 0,结果却是 40 print(funcs[1](10)) # 期待 10,结果 40 print(funcs[2](10)) # 期待 20,结果 40 print(funcs[3](10)) # 期待 30,结果 40 print(funcs[4](10)) # 期待 40,结果 40
所有函数都返回 40!因为 i 最后的值是 4,闭包里用的 i 永远指向循环结束后的那个同一个 i。这就是“延迟绑定”:闭包捕获的是变量本身,而不是定义时变量的值。为什么会这样?Python 的闭包捕获的是变量名(i),而不是值。当 lambda 真正被调用时,才会去查找 i 当前的值。那么我们要怎么避免出现这种情况呢,我们可以将变量赋值给默认参数,来提前绑定变量,参考代码如下
# 你想创建 0~4 每个数乘以自己的 5 个函数 funcs = [] for i in range(5): funcs.append(lambda x, z=i: x * z) # 测试一下 print(funcs[0](10)) # 你期待 0,结果却是 0 print(funcs[1](10)) # 期待 10,结果 10 print(funcs[2](10)) # 期待 20,结果 20 print(funcs[3](10)) # 期待 30,结果 30 print(funcs[4](10)) # 期待 40,结果 40
 
 
Python中还有不少这样的容易引发bug的语法特性,你有遇到过哪些呢?