Où je réponds à la question posée
Pourquoi Python ne le propose-t-il pas prêt à l'emploi?
Je soupçonne que cela a à voir avec le Zen de Python : "Il devrait y avoir une - et de préférence une seule - façon évidente de le faire." Cela créerait deux façons évidentes d'accéder aux valeurs des dictionnaires: obj['key']
et obj.key
.
Mises en garde et pièges
Il s'agit notamment d'un possible manque de clarté et de confusion dans le code. c'est-à-dire que ce qui suit pourrait être déroutant pour quelqu'un d' autre qui va maintenir votre code à une date ultérieure, ou même pour vous, si vous n'y revenez pas pendant un certain temps. Encore une fois, de Zen : "La lisibilité compte!"
>>> KEY = 'spam'
>>> d[KEY] = 1
>>> # Several lines of miscellaneous code here...
... assert d.spam == 1
Si d
est instancié ou KEY
est défini ou d[KEY]
est attribué loin de l'endroit où il d.spam
est utilisé, cela peut facilement entraîner une confusion quant à ce qui est fait, car ce n'est pas un idiome couramment utilisé. Je sais que cela pourrait me confondre.
De plus, si vous modifiez la valeur de la KEY
manière suivante (mais que vous manquez de changer d.spam
), vous obtenez maintenant:
>>> 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, ne vaut pas l'effort.
Autres éléments
Comme d'autres l'ont noté, vous pouvez utiliser n'importe quel objet lavable (pas seulement une chaîne) comme clé de dictée. Par exemple,
>>> d = {(2, 3): True,}
>>> assert d[(2, 3)] is True
>>>
est légal, mais
>>> 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'est pas. Cela vous donne accès à toute la gamme de caractères imprimables ou d'autres objets hachables pour vos clés de dictionnaire, que vous n'avez pas lorsque vous accédez à un attribut d'objet. Cela rend possible une magie telle qu'une métaclasse d'objet mis en cache, comme la recette du livre de recettes Python (Ch. 9) .
Où j'éditorialise
Je préfère l'esthétique de spam.eggs
Over spam['eggs']
(je pense que ça a l'air plus propre), et j'ai vraiment commencé à avoir envie de cette fonctionnalité quand j'ai rencontré le namedtuple
. Mais la commodité de pouvoir faire ce qui suit l'emporte.
>>> 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}
>>>
Ceci est un exemple simple, mais je me retrouve souvent à utiliser des dict dans des situations différentes de celles que j'utiliserais la obj.key
notation (c'est-à-dire lorsque j'ai besoin de lire les préférences dans un fichier XML). Dans d'autres cas, lorsque je suis tenté d'instancier une classe dynamique et de gifler certains attributs pour des raisons esthétiques, je continue à utiliser un dict pour la cohérence afin d'améliorer la lisibilité.
Je suis sûr que l'OP a résolu cela depuis longtemps à sa satisfaction, mais s'il veut toujours cette fonctionnalité, alors je lui suggère de télécharger l'un des packages de pypi qui le fournit:
Bunch est celui que je connais le mieux. Sous-classe dedict
, vous avez donc toutes ces fonctionnalités.
AttrDict semble également être assez bon, mais je ne le connais pas aussi bien et je n'ai pas parcouru la source avec autant de détails que j'ai Bunch .
- Addict est activement maintenu et fournit un accès semblable à attr et plus encore.
- Comme indiqué dans les commentaires de Rotareti, Bunch est obsolète, mais il existe un fork actif appelé Munch .
Cependant, afin d'améliorer la lisibilité de son code, je lui recommande fortement de ne pas mélanger ses styles de notation. S'il préfère cette notation, il doit simplement instancier un objet dynamique, y ajouter les attributs souhaités et l'appeler un jour:
>>> C = type('C', (object,), {})
>>> d = C()
>>> d.spam = 1
>>> d.eggs = 2
>>> d.ham = 3
>>> assert d.__dict__ == {'spam': 1, 'eggs': 2, 'ham': 3}
Où je mets à jour, pour répondre à une question de suivi dans les commentaires
Dans les commentaires (ci-dessous), Elmo demande:
Et si vous voulez aller plus loin? (en référence au type (...))
Bien que je n'aie jamais utilisé ce cas d'utilisation (encore une fois, j'ai tendance à utiliser imbriqué dict
, pour des raisons de cohérence), le code suivant fonctionne:
>>> 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
est très utile pour cela.