我在 Python 中发现了一个奇怪的错误。 key存在于dict中,但是还是报KeyError。 key存在于list(dict)中,但key不存在于dict中,为什么?
这是代码:
check_key = (predicate, item)
print(type(predicate))
print(type(item))
print(type(self.id_of_item))
print()
print("check_key: {}".format(check_key))
print("self.id_of_item: {}".format(self.id_of_item))
print("list(self.id_of_item): {}".format(list(self.id_of_item)))
print()
print("check_key in self.id_of_item? {}".format(check_key in self.id_of_item))
print("check_key in list(self.id_of_item)? {}".format(check_key in list(self.id_of_item)))
print("check_key == list(self.id_of_item)[-1]? {}".format(check_key == list(self.id_of_item)[-1]))
这是输出:
<class 'str'>
<class 'sympy.core.add.Add'>
<class 'dict'>
check_key: ('Equation', ll_bc - 4.0)
self.id_of_item: {('Equation', ll_bc - 4.0): 87}
list(self.id_of_item): [('Equation', ll_bc - 4.0)]
check_key in self.id_of_item? False
check_key in list(self.id_of_item)? True
check_key == list(self.id_of_item)[-1]? True
如何修改我的代码以通过键“check_key”访问值?
这个问题很难重现。我用库SymPy写了一个数学解题程序,6000道题只报了一个错。所以不可能提供一个可以直接运行的程序,我可以提供更多的输出。
这是新添加的代码:
print("id(check_key): {}".format(id(check_key)))
print("id(list(self.id_of_item)[-1]): {}".format(id(list(self.id_of_item)[-1])))
print("check_key is list(self.id_of_item)[-1]? {}".format(check_key is list(self.id_of_item)[-1]))
这是新的输出:
id(check_key): 2795752447488
id(list(self.id_of_item)[-1]): 2795752487168
check_key is list(self.id_of_item)[-1]? False
看来问题已经解决了。
check_key
和 list(self.id_of_item)[-1]
有不同的 id。我需要更多地了解“is”和“==”之间的区别。
然而,
id
不是唯一的问题!我添加了最小可运行代码来证明这一点。 id
和eq1
的eq2
虽然不同,但都映射到字典中的相同位置。
python==3.10.9
sympy==1.10.1
from sympy import symbols
ll_bc = symbols("ll_bc")
eq1 = ll_bc + 1.5 - 5.5
eq2 = ll_bc - 4.0
a_dict = {eq1: 1}
print("eq1: {}".format(eq1))
print("eq2: {}".format(eq2))
print("a_dict: {}".format(a_dict))
print()
print("eq1 is eq2: {}".format(eq1 is eq2))
print("eq1 == eq2: {}".format(eq1 == eq2))
print("eq2 in a_dict?: {}".format(eq2 in a_dict))
print("eq2 in list(a_dict)?: {}".format(eq2 in list(a_dict)))
输出:
eq1: ll_bc - 4.0
eq2: ll_bc - 4.0
a_dict: {ll_bc - 4.0: 1}
eq1 is eq2: False
eq1 == eq2: True
eq2 in a_dict?: True
eq2 in list(a_dict)?: True
终于重现了问题。这是浮点数的精度问题导致的错误。最小可运行代码如下:
python==3.10.9
sympy==1.10.1
from sympy import symbols, Float
def float_round(n, round_it=True):
if round_it:
return n.round(6)
return n
a = symbols("a")
target = a + float_round(Float(1.1))
eq1 = a - 1.0
eq2 = target - float_round(target.subs(a, 1.0))
a_dict = {eq1: 1}
print("eq1: {}".format(eq1))
print("eq2: {}".format(eq2))
print("eq1 is eq2?: {}".format(eq1 is eq2))
print("eq1 == eq2?: {}".format(eq1 == eq2))
print("eq2 in a_dict?: {}".format(eq2 in a_dict))
print("eq2 in list(a_dict)?: {}".format(eq2 in list(a_dict)))
输出:
eq1: a - 1.0
eq2: a - 1.0
eq1 is eq2?: False
eq1 == eq2?: True
eq2 in a_dict?: False
eq2 in list(a_dict)?: True
eq1
和eq2
看似一样,其实浮点数不同。这可能是我错误使用了Sympy库的相关函数造成的。我发现target.subs(a, 1.0)
的值是2.09999999403954
。正确的值应该是2.1
。我们可以将 round_it
int 函数 float_round
设置为 True
那么问题就消失了。
但我仍然从这个错误中学到了很多东西。显然,“in”操作是使用“dict.keys()”和“list”中的不同方法实现的。我需要进一步学习。
我想我已经大致明白问题是什么了。我会先提供一个更具可读性的最小可运行代码及其输出,然后再梳理出现这个问题的原因。 代码:
python==3.10.9
sympy==1.10.1
from sympy import symbols, Float
def float_round(n, round_it=True):
if round_it:
return n.round(6)
return n
a = symbols("a")
target = a + float_round(Float(1.1))
target_value = float_round(float_round(Float(1.1)) + 1)
print("different target_value:")
print(float_round(Float(1.1)) + 1)
print(float_round(Float(1.1)) + 1.0)
print(float_round(float_round(Float(1.1)) + 1))
print()
eq1 = target - target_value
eq2 = a - 1.0
print("eq1: {}".format(eq1))
print("eq2: {}".format(eq2))
print("hash(eq1): {}".format(hash(eq1)))
print("hash(eq2): {}".format(hash(eq2)))
print("eq1 is eq2?: {}".format(eq1 is eq2))
print("eq1 == eq2?: {}".format(eq1 == eq2))
print("hash(eq1) == hash(eq2)?: {}".format(hash(eq1) == hash(eq2)))
print()
a_dict = {eq1: 1}
print("a_dict: {}".format(a_dict))
print("eq1 == eq2?: {}".format(eq1 == eq2))
print("eq2 in a_dict?: {}".format(eq2 in a_dict))
print("eq2 in list(a_dict)?: {}".format(eq2 in list(a_dict)))
输出:
different target_value:
2.100000
2.09999999403954
2.100000
eq1: a - 1.0
eq2: a - 1.0
hash(eq1): 946170291633155547
hash(eq2): -2833311259663523353
eq1 is eq2?: False
eq1 == eq2?: True
hash(eq1) == hash(eq2)?: False
a_dict: {a - 1.0: 1}
eq1 == eq2?: True
eq2 in a_dict?: False
eq2 in list(a_dict)?: True
round
构造了三个浮点数。这三个浮点数看似等价,实则不然。所以用eq1
构造的方程target_value
也和eq2
不一样,虽然他们打印出来的结果是一致的eq1==eq2
。但是eq1
和eq2
有不同的哈希值。引用官方文档:“hash()
结果应该与相等一致。相等的对象应该具有相同的散列值,或者被标记为不可散列。”in
在Python中的实现。引用官方文档:“对于列表、元组、集合、frozenset、dict或collections.deque等容器类型,表达式x in y
等同于any(x is e or x == e for e in y)
。”in
是使用函数 __contains__
在 Python 中实现的。引用官方文档:“调用以实现成员资格测试运算符。如果项目在自身中,则应返回 true,否则返回 false。对于映射对象,这应考虑映射的键而不是值或键项对。”eq2 in list(a_dict)
是 True
因为 eq1 == eq2
。 eq2 in a_dict
是 False
因为 hash(eq1) != hash(eq2)
.