Update: dicts retaining insertion order is guaranteed for Python 3.7+
I want to use a .py file like a config file. So using the {...}
notation I can create a dictionary using strings as keys but the definition order is lost in a standard python dictionary.
My question: is it possible to override the {...}
notation so that I get an OrderedDict()
instead of a dict()
?
I was hoping that simply overriding dict constructor with OrderedDict (dict = OrderedDict
) would work, but it doesn't.
Eg:
dict = OrderedDict dictname = { 'B key': 'value1', 'A key': 'value2', 'C key': 'value3' } print dictname.items()
Output:
[('B key', 'value1'), ('A key', 'value2'), ('C key', 'value3')]
The only difference between dict() and OrderedDict() is that: OrderedDict preserves the order in which the keys are inserted. A regular dict doesn't track the insertion order and iterating it gives the values in an arbitrary order. By contrast, the order the items are inserted is remembered by OrderedDict.
The OrderedDict is a subclass of dict object in Python. The only difference between OrderedDict and dict is that, in OrderedDict, it maintains the orders of keys as inserted. In the dict, the ordering may or may not be happen. The OrderedDict is a standard library class, which is located in the collections module.
With CPython 2.7, using dict() to create dictionaries takes up to 6 times longer and involves more memory allocation operations than the literal syntax. Use {} to create dictionaries, especially if you are pre-populating them, unless the literal syntax does not work for your case.
Here's a hack that almost gives you the syntax you want:
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: Someone else discovered this independently, and has published the odictliteral
package on PyPI that provides a slightly more thorough implementation - use that package instead
To literally get what you are asking for, you have to fiddle with the syntax tree of your file. I don't think it is advisable to do so, but I couldn't resist the temptation to try. So here we go.
First, we create a module with a function my_execfile()
that works like the built-in execfile()
, except that all occurrences of dictionary displays, e.g. {3: 4, "a": 2}
are replaced by explicit calls to the dict()
constructor, e.g. dict([(3, 4), ('a', 2)])
. (Of course we could directly replace them by calls to collections.OrderedDict()
, but we don't want to be too intrusive.) Here's the 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
With this modification in place, we can modify the behaviour of dictionary displays by overwriting dict
. Here is an example:
# test.py from collections import OrderedDict print {3: 4, "a": 2} dict = OrderedDict print {3: 4, "a": 2}
Now we can run this file using my_execfile("test.py")
, yielding the output
{'a': 2, 3: 4} OrderedDict([(3, 4), ('a', 2)])
Note that for simplicity, the above code doesn't touch dictionary comprehensions, which should be transformed to generator expressions passed to the dict()
constructor. You'd need to add a visit_DictComp()
method to the DictDisplayTransformer
class. Given the above example code, this should be straight-forward.
Again, I don't recommend this kind of messing around with the language semantics. Did you have a look into the ConfigParser
module?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With