Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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')] 
like image 650
fdb Avatar asked Oct 24 '11 16:10

fdb


People also ask

What is the difference between OrderedDict and dict?

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.

What is the difference between OrderedDict and dictionary in Python?

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.

Should I use dict () or {}?

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.


2 Answers

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

like image 163
Eric Avatar answered Sep 23 '22 06:09

Eric


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?

like image 40
Sven Marnach Avatar answered Sep 19 '22 06:09

Sven Marnach