ordereddict ordered dict python dictionary override built-in ordereddictionary

python - ¿Anula la notación{...} entonces consigo un OrderedDict() en vez de un dict()?



python order dict by key (7)

A partir de Python 3.6, todos los diccionarios se ordenarán por defecto . Por ahora, este es un detalle de implementación de dict y no se debe confiar en él, pero es probable que se convierta en estándar después de v3.6.

El orden de inserción siempre se conserva en la nueva implementación de dict :

>>>x = {''a'': 1, ''b'':2, ''c'':3 } >>>list(x.keys()) [''a'', ''b'', ''c'']

A partir de python 3.6 **kwargs orden de **kwargs [PEP468] y orden de atributo de clase [ PEP520 ]. La nueva implementación compacta y ordenada del diccionario se usa para implementar el orden para ambos.

Quiero usar un archivo .py como un archivo de configuración. Entonces, usando la notación {...} puedo crear un diccionario usando cadenas como claves, pero el orden de definición se pierde en un diccionario de Python estándar.

Mi pregunta: ¿es posible anular la notación {...} para que obtenga un OrderedDict() lugar de un dict() ?

Esperaba que simplemente anulando el constructor dict con OrderedDict ( dict = OrderedDict ) funcionaría, pero no es así.

P.ej:

dict = OrderedDict dictname = { ''B key'': ''value1'', ''A key'': ''value2'', ''C key'': ''value3'' } print dictname.items()

Salida:

[(''B key'', ''value1''), (''A key'', ''value2''), (''C key'', ''value3'')]


Aquí hay un truco que casi te da la sintaxis que deseas:

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" ]

Editar: Alguien más descubrió esto de forma independiente, y publicó el paquete odictliteral en PyPI que proporciona una implementación un poco más completa: use esto en su lugar


La única solución que encontré es parchear python, haciendo que el objeto dict recuerde el orden de inserción.

Esto funciona para todo tipo de sintaxis:

x = {''a'': 1, ''b'':2, ''c'':3 } y = dict(a=1, b=2, c=3)

etc.

He tomado la implementación de ordereddict C desde https://pypi.python.org/pypi/ruamel.ordereddict/ y me he fusionado con el código python principal.

Si no le importa reconstruir el intérprete de Python, aquí hay un parche para Python 2.7.8: https://github.com/fwyzard/cpython/compare/2.7.8...ordereddict-2.7.8.diff . UN


Lo que está pidiendo es imposible, pero si un archivo de configuración en sintaxis JSON es suficiente, puede hacer algo similar con el módulo 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)])


Para obtener literalmente lo que estás pidiendo, tienes que jugar con el árbol de sintaxis de tu archivo. No creo que sea aconsejable hacerlo, pero no pude resistir la tentación de intentarlo. Así que, aquí vamos.

Primero, creamos un módulo con una función my_execfile() que funciona como el execfile() , excepto que todas las ocurrencias de las pantallas del diccionario, por ejemplo {3: 4, "a": 2} son reemplazadas por llamadas explícitas al constructor dict() , por ejemplo, dict([(3, 4), (''a'', 2)]) . (Por supuesto, podríamos reemplazarlos directamente por llamadas a collections.OrderedDict() , pero no queremos ser demasiado intrusivos.) Aquí está el código:

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

Con esta modificación en su lugar, podemos modificar el comportamiento de las pantallas de diccionario sobrescribiendo dict . Aquí hay un ejemplo:

# test.py from collections import OrderedDict print {3: 4, "a": 2} dict = OrderedDict print {3: 4, "a": 2}

Ahora podemos ejecutar este archivo usando my_execfile("test.py") , produciendo la salida

{''a'': 2, 3: 4} OrderedDict([(3, 4), (''a'', 2)])

Tenga en cuenta que, por simplicidad, el código anterior no toca las comprensiones del diccionario, que deberían transformarse en expresiones generadoras pasadas al constructor dict() . visit_DictComp() agregar un método visit_DictComp() a la clase DictDisplayTransformer . Dado el código de ejemplo anterior, esto debería ser sencillo.

De nuevo, no recomiendo este tipo de juego con la semántica del lenguaje. ¿Has ConfigParser un vistazo al módulo ConfigParser ?


Si lo que está buscando es una forma de obtener una sintaxis de inicialización fácil de usar, considere crear una subclase de OrderedDict y agregarle operadores que actualicen el dict, por ejemplo:

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 será: OrderedMap ([(1, 2), (4, 3), (''clave'', ''valor'')])

Otro ejemplo posible de azúcar sintáctico usando la sintaxis de corte:

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"]


OrderedDict no es "sintaxis estándar de Python"; sin embargo, un conjunto ordenado de pares clave-valor (en la sintaxis estándar de Python) es simplemente:

[(''key1 name'', ''value1''), (''key2 name'', ''value2''), (''key3 name'', ''value3'')]

Para obtener explícitamente un OrderedDict :

OrderedDict([(''key1 name'', ''value1''), (''key2 name'', ''value2''), (''key3 name'', ''value3'')])

Otra alternativa es ordenar dictname.items() , si eso es todo lo que necesita:

sorted(dictname.items())