#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
, и вы сохраняете ссылку на родительский элемент, чтобы вы могли перемещаться по дереву.