2013-08-27 2 views
2

Я еще не видел toggleable версии словаря dot-access.Переключаемый точечный доступ к Python dicts?

Мой первый проход попытка здесь не работает:

class DottableDict (dict): 

    def allowDotting (self, state=True): 
     if state: 
      self.__setattr__ = dict.__setitem__ 
      self.__getattr__ = dict.__getitem__ 
     else: 
      del self.__setattr__ 
      del self.__getattr__ 

>>> import dot 
>>> d = dot.DottableDict() 
>>> d.allowDotting() 
>>> d.foo = 'bar' 
>>> d 
{} 
>>> d.foo 
'bar' 
>>> d.__dict__ 
{'__setattr__': <slot wrapper '__setitem__' of 'dict' objects>, 'foo': 'bar', 
'__getattr__': <method '__getitem__' of 'dict' objects>} 
>>> d.allowDotting(False) 
>>> d.__dict__ 
{'foo': 'bar'} 

Я думаю, что подписи не совпадают между SetAttr и SetItem.

Мой второй проход также кажется, что он должен работать, но не может таким же образом:

class DottableDict (dict): 

    def dotGet (self, attr): 
     return dict.__getitem__(self, attr) 

    def dotSet (self, attr, value): 
     return dict.__setitem__(self, attr, value) 

    def allowDotting (self, state=True): 
     if state: 
      self.__getattr__ = self.dotGet 
      self.__setattr__ = self.dotSet 
     else: 
      del self.__setattr__ 
      del self.__getattr__ 
+0

Какое поведение вы желаете? – unutbu

+0

Мне иногда нравится использовать словарь dot-access - доступ к значениям экземпляра dict-instance через instance.foo как альтернативу экземпляру ['foo'] - но я хотел иметь возможность включать и отключать эту функцию во время выполнения. –

ответ

6

Если вы установили 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.

+0

Это больше похоже на количество кода, которое я ожидал бы от этого. Я просто выясняю, где питон вкладывает вещи, и не совсем это понимает. Почему установка 'self .__ dict__'' {} 'переключает объекты обратно на attr-запросы? –

+0

Я добавил несколько объяснений выше. – unutbu

+0

Мне показалось, что я видел, что '__dict__' не просматривался точными поисками, но я создал« класс A (объект): pass', затем 'a = A()', 'setattr (a, 'foo' , 'bar') 'и' print a.foo', и он выплевывает '' bar'', поэтому, я думаю, я ошибся. У меня больше вопросов, но я собираюсь прочитать ссылку, которую вы дали мне в первую очередь. Любые оставшиеся вопросы после этого я задам в качестве новых вопросов о SO. Я принимаю этот ответ, так как он отвечает на вопрос, является самым маленьким и гибким, и устраняет все граничные условия, с которыми я боролся. Благодарю. –

 Смежные вопросы

  • Нет связанных вопросов^_^