看起来是康庄大道,实则连环深坑
Python 的数据类型设计有不少反直觉的地方,bool 是 int 子类、浮点数精度、NaN 不等于自身……今天这 5 个坑,你可能不止踩过一个。
坑 1:字典键的哈希陷阱
这个坑我踩过,说出来你可能不信,当时 debug 了整整半天。
d = {True: 'yes', 1: 'one', 1.0: 'float_one'}
print(d)
print(len(d))你觉得输出是什么?想好了吗?
实际是:{'True': 'float_one'} 1
因为 True == 1 == 1.0 且它们的哈希值相同,Python 认为它们是同一个键。后面的值会覆盖前面的。字典使用哈希值和 == 来判断键是否相同。
正确写法:
# 如果需要区分,用字符串作为键
d = {'True': 'yes', '1': 'one', '1.0': 'float_one'}
print(len(d)) # 3
踩坑现场:配置文件解析时,布尔值和数字混在一起做字典键,配置项莫名其妙地丢失了。
坑 2:空元组和逗号(猜猜输出)
这个问题我提 MR 的时候被 review 过,后来发现团队里有三个人都踩过。
a = (1)
b = (1,)
print(type(a))
print(type(b))
A. 都是 tupleB. <class 'int'><class 'tuple'>C. 报错
答案是 B。在 Python 中,创建元组的是逗号而不是括号。(1) 只是一个带括号的整数表达式,(1,) 才是一个只有一个元素的元组。空元组用 () 创建。这个语法设计经常让新手困惑,也是代码审查中常见的 bug 来源。
# 单元素元组必须加逗号
a = (1,)
print(type(a)) # <class 'tuple'>
踩坑现场:函数返回 return (result) 以为返回了元组,调用方解包直接报错。
坑 3:空元组和逗号(猜猜输出)
这是我见过的最隐蔽的 bug 之一,代码能跑,测试能过,但结果就是不对。
a = (1)
b = (1,)
print(type(a))
print(type(b))
A. 都是 tupleB. <class 'int'><class 'tuple'>C. 报错
答案是 B。在 Python 中,创建元组的是逗号而不是括号。(1) 只是一个带括号的整数表达式,(1,) 才是一个只有一个元素的元组。空元组用 () 创建。这个语法设计经常让新手困惑,也是代码审查中常见的 bug 来源。
# 单元素元组必须加逗号
a = (1,)
print(type(a)) # <class 'tuple'>
踩坑现场:函数返回 return (result) 以为返回了元组,调用方解包直接报错。
坑 4:集合中的布尔和整数(猜猜输出)
这段代码看起来完全正确,但它在生产环境上悄无声息地出了问题。
s = {True, 1, 1.0}
print(s)
print(len(s))
A. {True, 1, 1.0} 长度3B. {True} 1C. 报错
答案是 B。和字典键一样,集合也用哈希值和 == 来判断元素是否重复。True == 1 == 1.0,所以集合认为它们是同一个元素,只保留第一个加入的。这个行为和字典键的逻辑完全一致,因为 set 底层就是用字典实现的(只有键没有值)。
# 如果需要区分,用字符串或元组包装
s = {('bool', True), ('int', 1), ('float', 1.0)}
print(len(s)) # 3
踩坑现场:统计不同类型的数据去重,发现数量总是比预期少。
坑 5:浮点数精度问题
这坑是经典中的经典,Python 面试里没有比它更高频的了。
print(0.1 + 0.2 == 0.3)
print(0.1 + 0.2)
你觉得输出是什么?想好了吗?
实际是:False 0.30000000000000004
这不是 Python 的 bug,是 IEEE 754 浮点数标准的特性。0.1 在二进制中是无限循环小数,存储时会有微小误差。几乎所有编程语言都有这个问题。
正确写法:
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
# 金融计算用 Decimal
from decimal import Decimal
踩坑现场:电商系统计算订单总价,偶尔出现一分钱的误差,被财务追着问。
好了,今天这 5 个就先聊到这里。代码里的坑数不清,一期一期慢慢说。
这些坑你都知道吗?评论区交流一下你的翻车经历。
下期预告:函数陷阱专场
关于作者
写了 10 年 Python,赶上了机器学习的热潮,又撞上了大模型的浪头,每次以为学明白了,行业又变了一次。不是什么大神,就是一个在互联网里摸爬滚打、把坑踩了个遍的老工人。还在写代码,还在折腾,还在想明白这个时代到底发生了什么。把自己踩过的坑、走过的弯路、看过的热闹,说给还在路上的人听。
关注「鲁叶的Python」,一起穿越迷茫期。