字典中存在Key,但仍然报KeyError

问题描述 投票:0回答:1

我在 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 sympy keyerror
1个回答
0
投票

我想我已经大致明白问题是什么了。我会先提供一个更具可读性的最小可运行代码及其输出,然后再梳理出现这个问题的原因。 代码:

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
  1. 这本质上是浮点数精度的问题
    我用库 SymPy 的函数
    round
    构造了三个浮点数。这三个浮点数看似等价,实则不然。所以用
    eq1
    构造的方程
    target_value
    也和
    eq2
    不一样,虽然他们打印出来的结果是一致的
  2. 我认为这可以看作是 SymPy 中的一个错误。
    在第二部分的输出中,我们可以看到
    eq1==eq2
    。但是
    eq1
    eq2
    有不同的哈希值。引用官方文档:“
    hash()
    结果应该与相等一致。相等的对象应该具有相同的散列值,或者被标记为不可散列。”
  3. 回到最初的问题。为什么key存在于list(dict)中,而key不存在于dict中?
    我们需要了解
    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)
.

© www.soinside.com 2019 - 2024. All rights reserved.