Какой метод вызывается при изменении значения во вложенном словаре?

#python #dictionary

#python #словарь

Вопрос:

Я пытаюсь создать постоянный словарь, перенося тип dict и особенно перегружая __setitem__ запись значений в файл.

 class PersistentStorage(dict):
    def __init__(self, file_name, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.path = file_name

    def write_dict(self):
        with open(self.path, "w") as f:
            json.dump(self, f)

    def __setitem__(self, key, value):
        super().__setitem__(key,value)
        self.write_dict() # writes 'self' to a json file.
 

Это работает, как и ожидалось, пока я не работаю с вложенными словарями:

 tester = PersistenStorage("example_path.json")

tester["key1"] = "Value for key 1"
# works and gets saved
tester["key2"] = {"subkey":""}
# works as well
tester["key2"]["subkey"] = "Added value"
# works locally but is not written to file (__setitem__ is not called)
 

Я также пытался перегрузить другие методы класса, но, похоже, я не могу найти тот, который вызывается в этом сценарии. Я также не понимаю, почему __setitem__ не вызывается.

Я также изучил typing и collections , но мне кажется, что прямое использование dict в качестве шаблонного класса является лучшим выбором, поскольку я не хочу изменять неотъемлемое функционирование типа, а просто расширяю операции чтения и записи.

Комментарии:

1. Вы используете обычный dict как значение key2 , не относящееся к вашему классу.

2. Да, но почему это проблема? В конце концов, я все еще изменяю элемент из своего класса, который должен сработать __setitem__ , не так ли?

3. Последняя строка кода не устанавливается tester["key2"] , поэтому нет, __setitem__ не вызывается.

4. @RemyMoll нет, вы не меняете элемент из своего класса . Вы получаете доступ к элементу из своего класса , который будет вызываться __getitem__ . Это выражение tester["key"] вычисляется до обычного dict объекта, после чего __setitem__ вызывается для этого

5. Хорошо, это проблема, которую исправляет shelve при настройке writeback = True ?

Ответ №1:

Вызов этого кода:

 tester["key2"]["subkey"] = "Added value"
 

эквивалентно следующим двум строкам:

 key2_value = tester["key2"]
key2_value["subkey"] = "Added value"
 

В первой строке PersistentStorage.__getitem__ вызывается (потому tester что является PersistentStorage )

Во второй строке dict.__setitem__ вызывается (потому key2_value что это a dict ).

Если вы хотите переопределить этот параметр, вам не следует использовать «обычный» dict as tester["key2"] .

Вы могли бы, например, изменить эту строку:

 tester["key2"] = {"subkey":""}
 

к этому:

 tester["key2"] = MyDictWithOverridden_setitem({"subkey":""})
 

А затем реализовать MyDictWithOverridden_setitem или как вы это называете, например

 class MyDictWithOverridden_setitem(dict):
    def __setitem__(self, key, value):
        do_something_special()
 

Комментарии:

1. Итак, я так понимаю, что мои надежды на простое решение просто исчезли. Я вижу свою ошибку, спасибо. Я все еще не могу понять, почему это происходит: как тогда обрабатывается словарь на уровне памяти? Если изменение значений во вложенном словаре не влияет на первое, тогда они будут отдельными объектами, и они просто символически связаны?

2. ключи и значения словаря @RemyMoll — это object s. dict имеет некоторые требования к ключам, чтобы иметь возможность эффективно их обрабатывать, но он ничего не знает и вообще не заботится о том, какие данные содержатся в значениях. На уровне памяти, если мы говорим о CPython, ссылки на значения PyObject * .

Ответ №2:

Я считаю, что вы можете добиться того, чего хотите, добавив в свою логику __setitem__ логику, которая преобразуется {"subkey":""} в нужный вам тип.

Это может потребовать изменения способа хранения данных в вашем классе, чтобы правильно воссоздать вложенную структуру dict .

Другими словами:

 tester["key2"] = {"subkey":""}
 

Эта строка преобразует вложенный dict в PersistentStorage , и вы сохраняете ссылку на родительский элемент, чтобы вы могли перемещаться по дереву.