作为 defaultdict 键的数据类:KeyError

A dataclass as a key to a defaultdict: KeyError

请解释并可能帮助如何使用冻结的 dataclass 密钥修复 defaultdict

示例:

"""Reproduce KeyError: defaultdict with dataclass keys"""

import collections
import dataclasses
import unittest


@dataclasses.dataclass(frozen=True)
class ReservationCoverageKey:
    """AWS RDS Reservation dimensions"""

    database_engine: str
    instance_family: str
    region: str


class TestCase(unittest.TestCase):
    """Main test cases"""

    def test_dict_with_dataclass_keys(self):
        """Test normal dict with dataclass key"""
        result = {}
        key = ReservationCoverageKey(
            database_engine="PostgreSQL",
            instance_family="db.t3",
            region="eu-west-1",
        )
        result[key] = 0
        result[key] += 1
        result[key] += 2
        self.assertEqual(result[key], 3)

    def test_defaultdict_with_dataclass_keys(self):
        """Test defaultdict with dataclass key"""
        result = collections.defaultdict(default_factory=int)
        key = ReservationCoverageKey(
            database_engine="PostgreSQL",
            instance_family="db.t3",
            region="eu-west-1",
        )
        result[key] += 1
        result[key] += 2
        self.assertEqual(result[key], 3)


if __name__ == "__main__":
    unittest.main()

复制:

$ python3 defaultdict_keyerror_reproduce.py 
E.
======================================================================
ERROR: test_defaultdict_with_dataclass_keys (__main__.TestCase)
Test defaultdict with dataclass key
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/var/tmp/sscce/defaultdict_keyerror_reproduce.py", line 41, in test_defaultdict_with_dataclass_keys
    result[key] += 1
KeyError: ReservationCoverageKey(database_engine='PostgreSQL', instance_family='db.t3', region='eu-west-1')

----------------------------------------------------------------------
Ran 2 tests in 0.000s

FAILED (errors=1)

带有 dict 的测试用例有效,但不适用于 defaultdict

是否有任何理由使用 result = collections.defaultdict(default_factory=int) 而不是 result = collections.defaultdict(int)

如果你这样改,它就会通过测试。我不知道为什么它不接受你声明的。

我认为它不接受默认键作为关键字参数。

示例:

import collections

result = collections.defaultdict(default_factory=int)
result2 = collections.defaultdict(int)
result3 = collections.defaultdict()

print(result)
print(result2)
print(result3)

#output:
> defaultdict(None, {'default_factory': <class 'int'>})
> defaultdict(<class 'int'>, {})
> defaultdict(None, {})

当您传递 default_factory 属性 时,它不会将其指定为默认键,因为它是位置参数。所以,您遇到了 KeyError。

collections.defaultdict(default_factory=int)创建一个没有默认值类型的默认dict,只有一个值(键"default_factory"处的类型int)因为default_factory只能传递作为位置参数。任何关键字参数都被视为字典的键值对:

defaultdict(None, {'default_factory': int})

您需要执行 collections.defaultdict(int),这会产生 defaultdict(int, {})