带有对象ID的特殊Python字典

3 投票
1 回答
2022 浏览
提问于 2025-04-16 18:31

我想创建一个特别的字典,用对象的ID作为键,像这样:

class ObjectIdDict(dict):

    def __setitem__(self, key, value):
        super(ObjectIdDict, self).__setitem__(id(key), value)

    def __getitem__(self, key):
        super(ObjectIdDict, self).__getitem__(id(key))

但是如果我运行下面的测试,就会出现错误:

class ObjectIdDictTest(unittest.TestCase):
    def test_get_and_set(self):
        dict_to_test = ObjectIdDict()
        class Something:
            def __init__(self):
                self.x = 1
        s = Something()
        dict_to_test[s.x] = "message"
        self.assertEqual(dict_to_test[s.x], "message")     

错误信息:

AssertionError: None != 'message'                   

这里出什么问题了?

背景:

我想创建这样的字典是因为我想存储每个对象字段的验证错误,并且希望避免使用字段名的字符串形式:domain_object.errors[domain_object.field1],否则如果用字符串形式的字段名(domain_object.errors["field1"]),在重构和代码补全时会很麻烦。

ΤΖΩΤΖΙΟΥ:

我敢肯定,使用ID不会给你带来任何好处。obj.field1= 1; print(id(obj.field1)); obj.field1= 2; print(id(obj.field1))

如果我不使用ID,键将是变量的,而不是它的地址。如果两个字段的值相同,就会导致错误:

def test_ordinary_dict(self):
    dict_to_test = {}
    class Something:
        def __init__(self):
            self.x = 1
            self.y = 1 # same value as self.x!
    s = Something()
    dict_to_test[s.x] = "message for x"
    dict_to_test[s.y] = "message for y"
    self.assertEqual(dict_to_test[s.x], "message for x")

    # fails because dict_to_test[s.x] == dict_to_test[1] what results in:
    # "message for y"

改变变量的值导致新的地址并不是关键,因为在那之后验证结果就不再有效了。

1 个回答

4

__getitem__ 这个方法必须返回一个结果:

def __getitem__(self, key):
  return super(ObjectIdDict, self).__getitem__(id(key))
  #^^^^^

如果没有写 return,那么默认返回的值就是 None,所以对于所有的键来说,oiddict[key] 的值就是 None

撰写回答