Обозначение точки стиля Javascript для словарных клавиш unpythonic?
Я начал использовать такие конструкции:
class DictObj(object):
def __init__(self):
self.d = {}
def __getattr__(self, m):
return self.d.get(m, None)
def __setattr__(self, m, v):
super.__setattr__(self, m, v)
Обновление: на основе этого потока я пересмотрел реализацию DictObj:
class dotdict(dict):
def __getattr__(self, attr):
return self.get(attr, None)
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
class AutoEnum(object):
def __init__(self):
self.counter = 0
self.d = {}
def __getattr__(self, c):
if c not in self.d:
self.d[c] = self.counter
self.counter += 1
return self.d[c]
где DictObj - словарь, к которому можно получить доступ через точную нотацию:
d = DictObj()
d.something = 'one'
Я нахожу его более эстетичным, чем d['something']
. Обратите внимание, что доступ к ключу undefined возвращает None вместо создания исключения, что тоже приятно.
Обновление: Smashery делает хороший момент, который mhawke расширяет для более легкого решения. Мне интересно, есть ли какие-либо нежелательные побочные эффекты использования dict вместо определения нового словаря; если нет, мне очень нравится решение mhawke.
AutoEnum является автоматически увеличивающимся Enum, используемым следующим образом:
CMD = AutoEnum()
cmds = {
"peek": CMD.PEEK,
"look": CMD.PEEK,
"help": CMD.HELP,
"poke": CMD.POKE,
"modify": CMD.POKE,
}
Оба работают хорошо для меня, но я чувствую себя бесстрашным в отношении них.
Это действительно плохие конструкции?
Ответы
Ответ 1
Это более простая версия вашего класса DictObj:
class DictObj(object):
def __getattr__(self, attr):
return self.__dict__.get(attr)
>>> d = DictObj()
>>> d.something = 'one'
>>> print d.something
one
>>> print d.somethingelse
None
>>>
Ответ 2
Ваш пример DictObj на самом деле довольно распространен. Дото-нотный доступ к объектным стилям может быть выигрышем, если вы имеете дело с "вещами, которые напоминают объекты, т.е. они имеют фиксированные имена свойств, содержащие только символы, действительные в идентификаторах Python. Такие вещи, как строки базы данных или представления форм, могут быть с пользой сохранены в этом виде объекта, что делает код более читаемым без превышения ['item access'].
Реализация немного ограничена - вы не получите синтаксиса nice конструктора dict, len(), сравнений, 'in', iteration или nice reprs. Вы можете, конечно, реализовать эти вещи самостоятельно, но в мире новых стилей вы можете получить их бесплатно, просто подклассифицируя dict:
class AttrDict(dict):
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
Чтобы получить поведение по умолчанию, просто подкласс Python 2.5 collection.defaultdict класс вместо dict.
Ответ 3
Что касается DictObj
, будет ли следующая работа для вас? Пустой класс позволит вам произвольно добавлять или заменять материал в контейнере.
class Container(object):
pass
>>> myContainer = Container()
>>> myContainer.spam = "in a can"
>>> myContainer.eggs = "in a shell"
Если вы не хотите бросать AttributeError, когда нет атрибута, что вы думаете о следующем? Лично я предпочел бы использовать dict для ясности или использовать предложение try/except.
class QuietContainer(object):
def __getattr__(self, attribute):
try:
return object.__getattr__(self,attribute)
except AttributeError:
return None
>>> cont = QuietContainer()
>>> print cont.me
None
Right?
Ответ 4
Насколько я знаю, классы Python используют словари для хранения своих атрибутов в любом случае (что скрыто от программиста), поэтому мне кажется, что то, что вы там сделали, эффективно эмулирует класс Python... с помощью python класс.
Ответ 5
Это не "неправильно" для этого, и может быть лучше, если ваши словари имеют сильную возможность превратиться в объекты в какой-то момент, но с осторожностью относятся к причинам наличия в скобках доступа в первую очередь:
- Доступ к точкам не может использовать ключевые слова в качестве ключей.
- Доступ к Dot должен использовать допустимые символы Python-identifier в ключах.
- Словари могут содержать любой хешируемый элемент, а не только строки.
Также имейте в виду, что вы всегда можете сделать ваши объекты доступными, как словари, если вы решите позже переключиться на объекты.
Для такого случая я бы по умолчанию использовал мантру "readability counts": предположительно другие программисты Python будут читать ваш код, и они, вероятно, не будут ожидать гибридов словаря/объекта во всем мире. Если это хорошее дизайнерское решение для конкретной ситуации, используйте его, но я не буду использовать его без необходимости делать это.
Ответ 6
Одним из основных недостатков использования чего-то вроде вашего DictObj является то, что вы либо должны ограничить допустимые ключи, либо у вас не могут быть методы на вашем DictObj, такие как .keys()
, .values()
, .items()
и т.д.
Ответ 7
Там есть симметрия между этим и этим:
class dotdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
Тот же интерфейс, просто реализованный по-другому...
class container(object):
__getitem__ = object.__getattribute__
__setitem__ = object.__setattr__
__delitem__ = object.__delattr__
Ответ 8
Мне нравится текстовая нотация намного лучше, чем словарные поля. Причина в том, что автозаполнение работает намного лучше.
Ответ 9
Неплохо, если это послужит вашей цели. "Практичность превосходит чистоту".
Я видел такой подход elserwhere (например, Paver), поэтому это можно считать общей потребностью (или желанием).
Ответ 10
Не забывайте Bunch.
Это ребенок словаря и может импортировать YAML или JSON или конвертировать любой существующий словарь в Bunch и наоборот. После слова "bunchify" d словарь получает точечные обозначения без потери каких-либо других методов словаря.
Ответ 11
Потому что вы просите о нежелательных побочных эффектах:
Недостаток заключается в том, что в визуальных редакторах, таких как eclipse + pyDev, вы увидите много переменных ошибки w90 для строк с использованием точечной нотации. Pydef не сможет найти такие определения "объекта" времени выполнения. Если в случае нормального словаря он знает, что вы просто получаете запись в словаре.
Вам нужно будет: 1) игнорировать эти ошибки и жить с красными крестами; 2) подавить эти предупреждения в строке за строкой, используя # @UndefinedVariable или 3) полностью отключить ошибку переменной undefined, в результате чего вы пропустите настоящие определения переменных undefined.