Если вы установили self.__dict__ = self
, то ДИКТ автоматически становится «dottable». Вы можете отключить «способность точки», установив self.__dict__ = {}
. Однако пары ключ-значение все равно будут доступны через индексирование. Эта идея исходит в основном из katrielalex's bio page:
class DottableDict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.__dict__ = self
def allowDotting(self, state=True):
if state:
self.__dict__ = self
else:
self.__dict__ = dict()
d = DottableDict()
d.allowDotting()
d.foo = 'bar'
print(d['foo'])
# bar
print(d.foo)
# bar
d.allowDotting(state=False)
print(d['foo'])
# bar
print(d.foo)
# AttributeError: 'DottableDict' object has no attribute 'foo'
Помните Дзен Python, однако:
Там должно быть одно-- и предпочтительно только один --obvious способ сделать это.
Ограничиваясь стандартным синтаксисом для доступа к dict, вы улучшаете удобочитаемость/ремонтопригодность для себя и других.
Как это работает:
При вводе d.foo
, Python ищет 'foo'
в ряде мест, один из которых находится в d.__dict__
. (Он также выглядит в d.__class__.__dict__
, и все __dict__
s из всех оснований, перечисленных в d.__class__.mro()
... Для получения полной информации об атрибутном поиске см. Это excellent article by Shalabh Chaturvedi).
В любом случае важным моментом для нас является то, что все пары ключ-значение в d.__dict__
можно получить с помощью точечной нотации.
Этот факт означает, что мы можем получить точки доступа к парам ключ-значение в d
, установив d.__dict__
в d
себя! d
, в конце концов, является dict, а d.__dict__
ожидает диктофонный объект. Обратите внимание, что это также эффективно с точки зрения памяти. Мы не копируем пары ключевых значений, мы просто направляем d.__dict__
на уже существующий dict.
Кроме того, назначив d.__dict__
на dict()
, мы фактически отключим точки доступа к парам ключ-значение в d
. (Это не полностью отключает точки доступа - пары ключ-значение в d.__class_.__dict__
, например, все еще можно получить через точечную нотацию. Спасибо, что это правда, или вы не сможете снова вызвать метод allowDotting
!)
Теперь вам может быть интересно, удаляет ли все пары ключ-значение в d
. Ответ - нет.
Пара ключей-значений не сохраняется в атрибуте __dict__
. Фактически, нормальный dict не имеет атрибута __dict__
.Поэтому установка d.__dict__ = {}
просто сбрасывает dict в нейтральное состояние. Мы могли бы использовать
del self.__dict__
insteaad из
self.__dict__ = dict()
тоже. Однако, поскольку DottableDict
присвоен атрибут __dict__
в __init__
, мне кажется более чистым, чтобы экземпляры DottableDict
всегда имели атрибут __dict__
.
В комментариях вы обратите внимание:
шаги: отключить доступ, установите d.foo в 'бар', включите доступ, d.foo это ушел отовсюду.
Чтобы сохранить атрибуты, такие как d.foo
, которые были установлены в то время как allowDotting
был выключен, вам необходимо хранить альтернативный ДИКТ, к которому self.__dict__
был установлен.
class DottableDict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self['_attributes'] = dict()
self.allowDotting()
def allowDotting(self, state=True):
if state:
self.update(self['_attributes'])
self.__dict__ = self
else:
self.__dict__ = self['_attributes']
d = DottableDict()
d.allowDotting(state=False)
d.foo = 'bar'
d.allowDotting(state=True)
print(d.foo)
# bar
d.allowDotting(state=False)
print(d.foo)
# bar
d.allowDotting(state=True)
print(d.foo)
# bar
По умолчанию атрибуты, начинающиеся с одного символа подчеркивания, понимаются как частные, детали реализации. Я расширяю соглашение здесь, вводя секретный ключ, '_attribute'
в dict.
Какое поведение вы желаете? – unutbu
Мне иногда нравится использовать словарь dot-access - доступ к значениям экземпляра dict-instance через instance.foo как альтернативу экземпляру ['foo'] - но я хотел иметь возможность включать и отключать эту функцию во время выполнения. –