Acho mais conveniente acessar chaves dict como em obj.foo
vez de obj['foo']
, então escrevi este trecho:
class AttributeDict(dict):
def __getattr__(self, attr):
return self[attr]
def __setattr__(self, attr, value):
self[attr] = value
No entanto, suponho que deve haver algum motivo pelo qual o Python não forneça essa funcionalidade imediatamente. Quais seriam as advertências e armadilhas do acesso a chaves de ditado dessa maneira?
python
dictionary
syntax
attributes
Izz ad-Din Ruhulessin
fonte
fonte
collections.namedtuple
é muito útil para isso.easydict.EasyDict
Respostas:
A melhor maneira de fazer isso é:
Alguns profissionais:
.keys()
funciona muito bem. A menos que, é claro, você atribua algum valor a eles, veja abaixo)AttributeError
vez deKeyError
Contras:
.keys()
a vontade não funcionar muito bem se eles se substituídos por dados de entradaE1123(unexpected-keyword-arg)
eE1103(maybe-no-member)
Uma breve explicação sobre como isso funciona
__dict__
.__dict__
precise ser "apenas um ditado simples", para que possamos atribuir qualquer subclasse dedict()
ao dicionário interno.AttrDict()
instância que estamos instanciando (como estamos__init__
).super()
o__init__()
método, garantimos que ele (já) se comporte exatamente como um dicionário, pois essa função chama todo o código de instanciação do dicionário .Uma razão pela qual o Python não fornece essa funcionalidade imediatamente
Conforme observado na lista "contras", isso combina o espaço para nome das chaves armazenadas (que podem vir de dados arbitrários e / ou não confiáveis!) Com o espaço para nome dos atributos do método dict interno. Por exemplo:
fonte
.
, você não pode violar as regras da linguagem :) E eu não gostariaAttrDict
de converter automaticamente os campos que contêm espaço em algo diferente.Você pode ter todos os caracteres de sequência legal como parte da chave se usar a notação de matriz. Por exemplo,
obj['!#$%^&*()_']
fonte
What would be the caveats and pitfalls of accessing dict keys in this manner?
(como atributos), e a resposta é que a maioria dos caracteres mostrados aqui não seria utilizável.De Esta outra questão SO há um grande exemplo de implementação que simplifica o código existente. E se:
Muito mais conciso e não deixa espaço para adulteração adicional de itens
__getattr__
e__setattr__
funções no futuro.fonte
d = AttributeDict(foo=1)
.d.bar = 1
o atributo bar é armazenado dentro do atributo dict, mas não no próprio dict. a impressãod
mostra apenas o item foo.d = AttributeDict(foo=1);d.bar = 1;print d
=>{'foo': 1, 'bar': 1}
Funciona para mim!__getattr__
método que gera umAttributeError
se o atributo dado não existir, caso contrário as coisas comogetattr(obj, attr, default_value)
não funcionam (ou seja, não voltardefault_value
seattr
não existe noobj
)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!"
Se
d
é instanciado ouKEY
definido oud[KEY]
atribuído longe de onded.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 alterard.spam
), agora obtém: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,
é legal, mas
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
excessospam['eggs']
(acho que parece mais limpo), e realmente comecei a desejar essa funcionalidade quando conheci onamedtuple
. Mas a conveniência de poder fazer o seguinte supera isso.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 .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:
Em que eu atualizo, para responder a uma pergunta de acompanhamento nos comentários
Nos comentários (abaixo), Elmo pergunta:
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:fonte
Advertência: por algumas razões, classes como esta parecem quebrar o pacote de multiprocessamento. Eu apenas lutei com esse bug por um tempo antes de encontrar este SO: Encontrar exceção no multiprocessamento python
fonte
Você pode obter uma classe de contêiner conveniente da biblioteca padrão:
para evitar ter que copiar em torno dos bits de código. Não há acesso padrão ao dicionário, mas é fácil recuperá-lo, se você realmente o quiser. O código no argparse é simples,
fonte
types.SimpleNamespace
docs.python.org/dev/library/types.html#types.SimpleNamespaceE se você quisesse uma chave que fosse um método, como
__eq__
ou__getattr__
?E você não conseguiria ter uma entrada que não começasse com uma letra; portanto, usar
0343853
como chave está fora.E se você não quisesse usar uma string?
fonte
pickle.dump
usa__getstate__
tuplas podem ser usadas chaves dict. Como você acessaria a tupla em sua construção?
Além disso, namedtuple é uma estrutura conveniente que pode fornecer valores por meio do acesso ao atributo.
fonte
Que tal Prodict , a pequena classe Python que escrevi para governar todos eles :)
Além disso, você obtém a conclusão automática de código , instanciações recursivas de objetos e conversão automática de tipos !
Você pode fazer exatamente o que pediu:
Exemplo 1: Dica de tipo
Exemplo 2: Conversão automática de tipo
fonte
Não funciona em geral. Nem todas as chaves dict válidas criam atributos endereçáveis ("a chave"). Então, você precisará ter cuidado.
Objetos Python são basicamente dicionários. Portanto, duvido que haja muito desempenho ou outra penalidade.
fonte
Isso não aborda a questão original, mas deve ser útil para pessoas que, como eu, acabam aqui quando procuram uma biblioteca que ofereça essa funcionalidade.
Addict , é uma ótima biblioteca para isso: https://github.com/mewwts/addict cuida de muitas preocupações mencionadas nas respostas anteriores.
Um exemplo dos documentos:
Com viciado:
fonte
Eu me perguntei qual seria o estado atual das "chaves de ditado como attr" no ecossistema python. Como vários comentadores apontaram, isso provavelmente não é algo que você queira criar do zero , pois existem várias armadilhas e espingardas, algumas delas muito sutis. Além disso, eu não recomendaria usar
Namespace
como classe base, já estive nesse caminho, não é bonito.Felizmente, existem vários pacotes de código aberto que fornecem essa funcionalidade, prontos para a instalação do pip! Infelizmente, existem vários pacotes. Aqui está uma sinopse, em dezembro de 2019.
Candidatos (comprometimento mais recente com o domínio | #commits | #contribs | cobertura%):
Não é mais mantida ou sub-mantida:
Atualmente, recomendo arrebentar ou viciado . Eles têm o maior número de confirmações, contribuidores e lançamentos, sugerindo uma base de código de código aberto saudável para cada um. Eles possuem o leia-me.md mais bonito, 100% de cobertura e um conjunto de testes de boa aparência.
Eu não tenho um cachorro nesta corrida (por enquanto!), Além de ter rodado meu próprio código de dict / attr e perdido uma tonelada de tempo porque eu não estava ciente de todas essas opções :). Posso contribuir para viciar / arrebentar no futuro, pois prefiro ver um pacote sólido do que um monte de pacotes fragmentados. Se você gosta deles, contribua! Em particular, parece que munch poderia usar um distintivo codecov e o viciado poderia usar um distintivo da versão python.
profissionais viciados:
contras do viciado:
typing.Dict
se vocêfrom addict import Dict
munch profissionais:
munch contras:
Em que editorializo
Muitas luas atrás, quando eu usava editores de texto para escrever python, em projetos somente comigo ou com outro desenvolvedor, eu gostava do estilo dict-attrs, da capacidade de inserir chaves apenas declarando
foo.bar.spam = eggs
. Agora, trabalho em equipes e uso um IDE para tudo, e me afastei desses tipos de estruturas de dados e da digitação dinâmica em geral, em favor da análise estática, técnicas funcionais e dicas de tipo. Comecei a experimentar essa técnica, subclassificando o Pstruct com objetos de meu próprio design:Isso fornece um objeto que ainda se comporta como um ditado, mas também permite acessar chaves como atributos, de uma maneira muito mais rígida. A vantagem aqui é que eu (ou os infelizes consumidores do seu código) sei exatamente quais campos podem ou não existir, e o IDE pode preencher automaticamente os campos. Subclassing vanilla também
dict
significa que a serialização do json é fácil. Eu acho que a próxima evolução nessa idéia seria um gerador de protobuf personalizado que emite essas interfaces, e um bom argumento é que você obtém estruturas de dados em várias linguagens e IPC via gRPC de graça.Se você optar por atribuir atributos, é essencial documentar quais campos são esperados, para sua própria sanidade (e de seus colegas de equipe).
Sinta-se livre para editar / atualizar este post para mantê-lo recente!
fonte
addict
é que ele não gera exceções quando você digita um atributo incorretamente, pois retornará um novoDict
(isso é necessário para que foo.abc = 'bar' funcione).Aqui está um pequeno exemplo de registros imutáveis usando o built-in
collections.namedtuple
:e um exemplo de uso:
fonte
Apenas para adicionar alguma variedade à resposta, o sci-kit learn implementou isso como
Bunch
:Tudo o que você precisa é obter os métodos
setattr
egetattr
- asgetattr
verificações de chaves de ditado e a verificação de atributos reais. Estasetstaet
é uma correção para correção de "cachos" de decapagem / remoção de selos - se estiver novamente incorreto, verifique https://github.com/scikit-learn/scikit-learn/issues/6196fonte
Não há necessidade de escrever seus próprios já que setattr () e getattr () já existem.
A vantagem dos objetos de classe provavelmente entra em jogo na definição e herança de classes.
fonte
Eu criei isso com base na entrada deste segmento. Porém, eu preciso usar odict, então tive que substituir get e set attr. Eu acho que isso deve funcionar para a maioria dos usos especiais.
O uso fica assim:
A classe:
Esse é um padrão bem legal já mencionado no thread, mas se você quiser apenas pegar um ditado e convertê-lo em um objeto que funcione com o preenchimento automático em um IDE, etc:
fonte
Aparentemente, agora existe uma biblioteca para isso - https://pypi.python.org/pypi/attrdict - que implementa essa funcionalidade exata, mais mesclagem recursiva e carregamento de json. Pode valer a pena dar uma olhada.
fonte
É isso que eu uso
fonte
namedtuple('Args', list(args.keys()))(**args)
Você pode fazer isso usando essa classe que acabei de criar. Com essa classe, você pode usar o
Map
objeto como outro dicionário (incluindo serialização json) ou com a notação de ponto. Espero ajudá-lo:Exemplos de uso:
fonte
dict
métodos, por exemplo:m=Map(); m["keys"] = 42; m.keys()
giveTypeError: 'int' object is not callable
.field/attribute
e não ummethod
, mas se você atribuir um método a um número, poderá acessar esse métodom.method()
.Deixe-me postar outra implementação, que se baseia na resposta do Kinvais, mas integra idéias do AttributeDict proposto em http://databio.org/posts/python_AttributeDict.html .
A vantagem desta versão é que ela também funciona para dicionários aninhados:
fonte
fonte
A solução é:
fonte
Como o @Henry sugere, um motivo pelo qual o acesso pontilhado não pode ser usado nos dict é que ele limita os nomes das chaves do dict a variáveis válidas para python, restringindo, assim, todos os nomes possíveis.
A seguir, exemplos de por que o acesso pontilhado não seria útil em geral, dado um ditado
d
:Validade
Os seguintes atributos seriam inválidos no Python:
Estilo
As convenções do PEP8 imporiam uma restrição leve à nomeação de atributos:
A. Nomes de palavras-chave reservadas (ou funções internas):
B. A regra de caso sobre métodos e nomes de variáveis :
Às vezes, essas preocupações são levantadas em bibliotecas como pandas , o que permite o acesso pontilhado das colunas DataFrame pelo nome. O mecanismo padrão para resolver restrições de nomenclatura também é uma notação de matriz - uma string entre colchetes.
Se essas restrições não se aplicarem ao seu caso de uso, há várias opções nas estruturas de dados de acesso pontilhado .
fonte
Você pode usar o dict_to_obj https://pypi.org/project/dict-to-obj/ Ele faz exatamente o que você pediu
fonte
.idea
e qualquer arquivo específico do usuário ou gerado pelo IDE no seu.gitignore
.Esta não é uma resposta 'boa', mas achei que era bacana (não lida com ditados aninhados na forma atual). Simplesmente envolva seu ditado em uma função:
Agora você tem uma sintaxe um pouco diferente. Para acessar os itens de ditado como os atributos
f.key
. Para acessar os itens de dict (e outros métodos de dict) da maneira usual,f()['key']
podemos atualizar convenientemente o dict chamando f com argumentos de palavra-chave e / ou um dicionárioExemplo
E aí está. Ficarei feliz se alguém sugerir vantagens e desvantagens desse método.
fonte
Conforme observado por Doug, há um pacote Bunch que você pode usar para obter a
obj.key
funcionalidade. Na verdade, há uma versão mais nova chamadaNeoBunch
Ele possui um ótimo recurso que converte seu ditado em um objeto NeoBunch através de sua função neobunchify . Eu uso muito os modelos Mako e a transmissão de dados como objetos do NeoBunch os torna muito mais legíveis; portanto, se você acabar usando um ditado normal no seu programa Python, mas quiser a notação de pontos em um modelo Mako, poderá usá-lo dessa maneira:
E o modelo Mako pode se parecer com:
fonte
A maneira mais fácil é definir uma classe, vamos chamá-la de espaço para nome. que usa o objeto dict .update () no dict. Então, o ditado será tratado como um objeto.
fonte