web-dev-qa-db-fra.com

Remplacez la notation {...} afin d'obtenir un OrderedDict () au lieu d'un dict ()?

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')]
39
fdb

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

73
Eric

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?

37
Sven Marnach

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())
12
Austin Marshall

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)])
5
Magnus Hoff

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

4
fwyzard

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.

2
Nick Sweeting

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"]
0
Or Weis