Je veux utiliser un fichier .py comme un fichier de configuration. Donc, en utilisant la notation {...}
, je peux créer un dictionnaire en utilisant des chaînes comme clés, mais l’ordre de définition est perdu dans un dictionnaire python standard.
Ma question: est-il possible de remplacer la notation {...}
afin d'obtenir une OrderedDict()
au lieu d'une dict()
?
J'espérais que le simple fait de remplacer le constructeur dict avec OrderedDict (dict = OrderedDict
) fonctionnerait, mais cela ne fonctionne pas.
Par exemple:
dict = OrderedDict
dictname = {
'B key': 'value1',
'A key': 'value2',
'C key': 'value3'
}
print dictname.items()
Sortie:
[('B key', 'value1'), ('A key', 'value2'), ('C key', 'value3')]
Voici un hack qui vous donne presque la syntaxe que vous voulez:
class _OrderedDictMaker(object):
def __getitem__(self, keys):
if not isinstance(keys, Tuple):
keys = (keys,)
assert all(isinstance(key, slice) for key in keys)
return OrderedDict([(k.start, k.stop) for k in keys])
ordereddict = _OrderedDictMaker()
from nastyhacks import ordereddict
menu = ordereddict[
"about" : "about",
"login" : "login",
'signup': "signup"
]
Edit: Quelqu'un d’autre a découvert cela de façon indépendante et a publié le paquet odictliteral
sur PyPI qui fournit une implémentation légèrement plus complète - utilisez plutôt ceci
Pour obtenir littéralement ce que vous demandez, vous devez manipuler l’arbre de syntaxe de votre fichier. Je ne pense pas qu'il soit conseillé de le faire, mais je ne pouvais pas résister à la tentation d'essayer. Alors on y va.
Tout d'abord, nous créons un module avec une fonction my_execfile()
qui fonctionne comme le execfile()
intégré, sauf que toutes les occurrences d'affichages du dictionnaire, p. Ex. {3: 4, "a": 2}
sont remplacés par des appels explicites au constructeur dict()
, par exemple. dict([(3, 4), ('a', 2)])
. (Bien sûr, nous pourrions les remplacer directement par des appels à collections.OrderedDict()
, mais nous ne voulons pas être trop intrusifs.) Voici le code:
import ast
class DictDisplayTransformer(ast.NodeTransformer):
def visit_Dict(self, node):
self.generic_visit(node)
list_node = ast.List(
[ast.copy_location(ast.Tuple(list(x), ast.Load()), x[0])
for x in Zip(node.keys, node.values)],
ast.Load())
name_node = ast.Name("dict", ast.Load())
new_node = ast.Call(ast.copy_location(name_node, node),
[ast.copy_location(list_node, node)],
[], None, None)
return ast.copy_location(new_node, node)
def my_execfile(filename, globals=None, locals=None):
if globals is None:
globals = {}
if locals is None:
locals = globals
node = ast.parse(open(filename).read())
transformed = DictDisplayTransformer().visit(node)
exec compile(transformed, filename, "exec") in globals, locals
Avec cette modification en place, nous pouvons modifier le comportement des affichages de dictionnaire en écrasant dict
. Voici un exemple:
# test.py
from collections import OrderedDict
print {3: 4, "a": 2}
dict = OrderedDict
print {3: 4, "a": 2}
Maintenant, nous pouvons exécuter ce fichier en utilisant my_execfile("test.py")
, donnant la sortie
{'a': 2, 3: 4}
OrderedDict([(3, 4), ('a', 2)])
Notez que par souci de simplicité, le code ci-dessus ne touche pas les compréhensions de dictionnaire, qui devraient être transformées en expressions génératrices passées au constructeur dict()
. Vous devez ajouter une méthode visit_DictComp()
à la classe DictDisplayTransformer
. Étant donné l'exemple de code ci-dessus, cela devrait être simple.
Encore une fois, je ne recommande pas ce genre de bricolage avec la sémantique linguistique. Avez-vous consulté le module ConfigParser
?
OrderedDict
n'est pas "la syntaxe python standard", cependant, un ensemble ordonné de paires clé-valeur (dans la syntaxe python standard) est simplement:
[('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')]
Pour obtenir explicitement une OrderedDict
:
OrderedDict([('key1 name', 'value1'), ('key2 name', 'value2'), ('key3 name', 'value3')])
Une autre solution consiste à trier dictname.items()
, si c'est tout ce dont vous avez besoin:
sorted(dictname.items())
Ce que vous demandez est impossible, mais si un fichier de configuration dans JSON syntaxe est suffisant, vous pouvez faire quelque chose de similaire avec le module json
:
>>> import json, collections
>>> d = json.JSONDecoder(object_pairs_hook = collections.OrderedDict)
>>> d.decode('{"a":5,"b":6}')
OrderedDict([(u'a', 5), (u'b', 6)])
La solution que j'ai trouvée consiste à appliquer un correctif à Python lui-même, afin que l'objet dict
se souvienne de l'ordre d'insertion.
Ceci fonctionne alors pour tous les types de syntaxes:
x = {'a': 1, 'b':2, 'c':3 }
y = dict(a=1, b=2, c=3)
etc.
J'ai pris l'implémentation ordereddict
C de https://pypi.python.org/pypi/ruamel.ordereddict/ et j'ai fusionné avec le code python principal.
Si vous ne voulez pas reconstruire l’interpréteur python, voici un correctif pour Python 2.7.8: https://github.com/fwyzard/cpython/compare/2.7.8...ordereddict -2.7.8.diff . A
Depuis python 3.6, tous les dictionnaires seront ordonné par défaut . Pour l'instant, il s'agit d'un détail d'implémentation de dict
et ne doit pas être utilisé, mais il deviendra probablement standard après la v3.6.
L'ordre d'insertion est toujours conservé dans la nouvelle implémentation dict
:
>>>x = {'a': 1, 'b':2, 'c':3 }
>>>list(x.keys())
['a', 'b', 'c']
Depuis python 3.6, **kwargs
order [PEP468] et l'ordre d'attribut de classe [ PEP520 ] sont préservés. La nouvelle implémentation compacte du dictionnaire ordonné est utilisée pour implémenter le classement de ces deux éléments.
Si vous recherchez un moyen d'obtenir une syntaxe d'initialisation facile à utiliser, envisagez de créer une sous-classe de OrderedDict et d'y ajouter des opérateurs qui mettent à jour le dict, par exemple:
from collections import OrderedDict
class OrderedMap(OrderedDict):
def __add__(self,other):
self.update(other)
return self
d = OrderedMap()+{1:2}+{4:3}+{"key":"value"}
d sera be- OrderedMap ([(1, 2), (4, 3), ('clé', 'valeur')])
Un autre exemple syntaxique-sucre utilisant la syntaxe de découpage:
class OrderedMap(OrderedDict):
def __getitem__(self, index):
if isinstance(index, slice):
self[index.start] = index.stop
return self
else:
return OrderedDict.__getitem__(self, index)
d = OrderedMap()[1:2][6:4][4:7]["a":"H"]