Python看起来简单
但这5个细节,稍不注意就踩坑
踩一次浪费1小时排查
1、可变默认参数
def add_item(item, lst=[]): # ❌ 永远用 None lst.append(item) return lst# 正确写法def add_item(item, lst=None): if lst is None: lst = [] lst.append(item) return lstadd_item(1) # → [1]add_item(2) # → [2],而不是 [1, 2]
⚠️ 函数参数默认值是列表/字典时,多次调用会共享同一个对象。
2、浅拷贝 vs 深拷贝
import copya = [[1,2], [3,4]]b = a # ❌ 引用同一个对象b[0][0] = 'b'print(a[0][0]) # → b(a也被改了!)a = [[1,2], [3,4]]c = a.copy() # ❌ 浅拷贝,只拷贝第一层c[0][0] = 'c'print(a[0][0]) # → c(a也被改了!)a = [[1,2], [3,4]]d = copy.deepcopy(a) # ✅ 深拷贝,完全独立d[0][0] = 'd'print(a[0][0]) # → 1(a不受影响)
⚠️ copy() 只拷贝一层,嵌套结构内部还是共享的,必须用 deepcopy()。
3、is 和 == 有区别
# 值是值,内存是内存a = 1000b = 1000print(a == b) # ✅ True(值相等)print(a is b) # ❌ False(不是同一个对象)# 字符串、列表单独看s1 = "hello"s2 = "hello"print(s1 is s2) # ✅ True(字符串有驻留机制)print([1,2] is [1,2]) # ❌ False
⚠️ is 比较的是对象身份(内存地址),== 比较的是值。判断 None 必须用 is None。
4、循环变量的引用
# 陷阱:闭包中的循环变量funcs = []for i in range(3): funcs.append(lambda: print(i)) # ❌ 所有函数都打印2funcs[0]() # → 2funcs[1]() # → 2funcs[2]() # → 2# 正确写法:用默认参数捕获当前值funcs = []for i in range(3): funcs.append(lambda i=i: print(i)) # ✅funcs[0]() # → 0funcs[1]() # → 1funcs[2]() # → 2
⚠️ 闭包捕获的是变量引用,不是值。循环结束后所有lambda都看到同一个i。
5、字典get()不存在的键
d = {"a": 1, "b": 2}print(d["c"]) # ❌ KeyErrorprint(d.get("c")) # ✅ 返回 Noneprint(d.get("c", 0)) # ✅ 返回默认值 0# ⚠️ 注意:get()返回None时不会报错,但None不是0print(d.get("c") + 1) # → ❌ TypeErrorprint(d.get("c", 0) + 1) # → 0 + 1print((d.get("c") or 0) + 1) # ✅ 用 or 处理
⚠️ get() 不存在的键返回 None 而不是报错,容易被忽略导致后续运算出错。
-------------------------它是数字世界里的一把杀猪刀
却总能巧夺天工
它的世界是纯粹0、1组合
却总能创造无尽幻想
......
本公众号关注数据价值分析、编程学习,将不定期更新社会热点数据分析结果、编程技巧,分享数据分析工具、方法、学习等内容,欢迎有兴趣的小伙伴加入。