fdb fdb - 2 months ago 6
Python Question

Override the {...} notation so i get an OrderedDict() instead of a dict()?

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
instead of a

I was hoping that simply overriding dict constructor with OrderedDict (
dict = OrderedDict
) would work, but it doesn't.


dict = OrderedDict
dictname = {
'B key': 'value1',
'A key': 'value2',
'C key': 'value3'

print dictname.items()


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


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):
        list_node = ast.List(
            [ast.copy_location(ast.Tuple(list(x), ast.Load()), x[0])
             for x in zip(node.keys, node.values)],
        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?