Em que eu respondo à pergunta que foi feita
Por que o Python não o oferece imediatamente?
Suspeito que isso tenha a ver com o Zen do Python : "Deve haver uma - e de preferência apenas uma - maneira óbvia de fazer isso". Isso criaria duas maneiras óbvias de acessar valores dos dicionários: obj['key']
eobj.key
.
Advertências e armadilhas
Isso inclui possível falta de clareza e confusão no código. ou seja, o seguinte pode ser confuso para outra pessoa que deseja manter seu código em uma data posterior ou mesmo para você, se você não voltar a usá-lo por um tempo. Mais uma vez, do Zen : "A legibilidade conta!"
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Se d
é instanciado ou KEY
definido ou d[KEY]
atribuído longe de onde d.spam
está sendo usado, pode facilmente causar confusão sobre o que está sendo feito, pois esse não é um idioma comumente usado. Eu sei que teria o potencial de me confundir.
Além disso, se você alterar o valor da KEY
seguinte forma (mas não alterar d.spam
), agora obtém:
>>> KEY = 'foo'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
AttributeError: 'C' object has no attribute 'spam'
OMI, não vale o esforço.
Outros itens
Como outros observaram, você pode usar qualquer objeto hash (não apenas uma string) como uma chave de ditado. Por exemplo,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
é legal, mas
>>> C = type('C', (object,), {(2, 3): True})
>>> d = C()
>>> assert d.(2, 3) is True
File "<stdin>", line 1
d.(2, 3)
^
SyntaxError: invalid syntax
>>> getattr(d, (2, 3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
>>>
não é. Isso fornece acesso a todo o intervalo de caracteres imprimíveis ou outros objetos hash para suas chaves de dicionário, que você não possui ao acessar um atributo de objeto. Isso possibilita a mágica de uma metaclasse de objeto em cache, como a receita do Python Cookbook (Cap. 9) .
Em que editorializo
Eu prefiro a estética do que o spam.eggs
excesso spam['eggs']
(acho que parece mais limpo), e realmente comecei a desejar essa funcionalidade quando conheci o namedtuple
. Mas a conveniência de poder fazer o seguinte supera isso.
>>> KEYS = 'spam eggs ham'
>>> VALS = [1, 2, 3]
>>> d = {k: v for k, v in zip(KEYS.split(' '), VALS)}
>>> assert d == {'spam': 1, 'eggs': 2, 'ham': 3}
>>>
Este é um exemplo simples, mas frequentemente me vejo usando dictos em situações diferentes das que usaria obj.key
notação (ou seja, quando preciso ler prefs de um arquivo XML). Em outros casos, onde sou tentado a instanciar uma classe dinâmica e a atribuir alguns atributos por razões estéticas, continuo usando um ditado de consistência para melhorar a legibilidade.
Tenho certeza que o OP há muito tempo resolveu isso para sua satisfação, mas se ele ainda deseja essa funcionalidade, sugiro que baixe um dos pacotes do pypi que o fornece:
Bunch é com quem eu estou mais familiarizado. Subclasse dedict
, para que você tenha toda essa funcionalidade.
O AttrDict também parece bom demais , mas eu não estou tão familiarizado com isso e não procurei a fonte com tantos detalhes quanto o Bunch .
- O Addict é mantido ativamente e fornece acesso semelhante a atr e muito mais.
- Conforme observado nos comentários de Rotareti, o Bunch foi descontinuado, mas há um fork ativo chamado Munch .
No entanto, para melhorar a legibilidade de seu código, recomendo fortemente que ele não misture seus estilos de notação. Se ele preferir essa notação, ele deve simplesmente instanciar um objeto dinâmico, adicionar seus atributos desejados e chamá-lo de um dia:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
Em que eu atualizo, para responder a uma pergunta de acompanhamento nos comentários
Nos comentários (abaixo), Elmo pergunta:
E se você quiser ir mais fundo? (referente ao tipo (...))
Embora eu nunca tenha usado esse caso de uso (novamente, eu tendem a usar aninhados dict
, por consistência), o código a seguir funciona:
>>> C = type('C', (object,), {})
>>> d = C()
>>> for x in 'spam eggs ham'.split():
... setattr(d, x, C())
... i = 1
... for y in 'one two three'.split():
... setattr(getattr(d, x), y, i)
... i += 1
...
>>> assert d.spam.__dict__ == {'one': 1, 'two': 2, 'three': 3}
collections.namedtuple
é muito útil para isso.