Question

I have the following code:

from functions import *

powers = AutoVivification()
powers[1] = {'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1}

print powers[1]

my autovivification is the following (taken from: What is the best way to implement nested dictionaries?):

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __getitem__(self, item):
        try:
            return dict.__getitem__(self, item)
        except KeyError:
            value = self[item] = type(self)()
            return value

it prints the following:

{'A': 1, 'c1': 0.5, 'gamma': 1, 'lambda': 1}

notice the order has been changed. it's now alphabetical. is there any way to prevent this from happening? sory, wasn't clear enough: (without changing the keys, and using the property of autovivificaion of "making arbitrarilly expanding the dictionary super easy")

Was it helpful?

Solution

As the documentation says:

Keys and values are iterated over in an arbitrary order which is non-random, varies across Python implementations, and depends on the dictionary’s history of insertions and deletions.

In other words, dictionaries have no inherent order. And you can see that without all your complicated extras:

>>> print {'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1}
{'A': 1, 'c1': 0.5, 'gamma': 1, 'lambda': 1}

If you want a dict that maintains its keys in the order of insertion, you can do that with OrderedDict.


However, that's not enough to help you in this case. If you construct a dict (which has arbitrary order), then pass that to an OrderedDict, all you're doing is freezing that initial arbitrary order:

>>> from collections import OrderedDict
>>> print OrderedDict({'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1})
OrderedDict([('A', 1), ('c1', 0.5), ('gamma', 1), ('lambda', 1)])

The repr of that OrderedDict should give you a clue to how you can create an OrderedDict with an order for its initial values: create it from a sequence, where each element is a key-value pairs:

>>> print OrderedDict([('c1', 0.5), ('gamma', 1), ('lambda', 1), ('A', 1)])
OrderedDict([('c1', 0.5), ('gamma', 1), ('lambda', 1), ('A', 1)])

If you want to auto-vivify the OrderedDict, you can do that by using OrderedDict instead of dict in your existing class. However, there are a few problems with the class you're using that you might want to consider. In particular, you really want to use super rather than classic-style calls on parent-class methods; if you'd done that, you could just define class AutoVivifiedOrderedDict(OrderedDict, AutoVivification): pass and be done with it! Also, I don't think your class will pickle properly as-is.


If you use a defaultdict for autovivification, it's already taken care of all the tricky bits:

def AutoVivification():
    return defaultdict(AutoVivification)

If you then want to add ordering to that, you will need an OrderedDefaultDict, so you can do this:

def OrderedAutoVivification():
    return OrderedDefaultDict(AutoVivification)

If you don't know how to create OrderedDefaultDict yourself, search for recipes. (You can almost just inherit from both classes… except for the fact that they have different signatures, so you need to think through what your __init__ should look like and explicitly call the right base initializers with the right arguments. See this question for some discussion.)

OTHER TIPS

Dictionaries are not ordered in Python. It won't retain its order once sorted. Instead, use

from collections import OrderedDict

powers = OrderedDict()
powers[1] = {'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1}

print powers[1]

You could add another key to specify the order...

import operator

powers = {0: ['c1', 0.5], 1: ['gamma', 1], 2: ['lambda', 1], 3: ['A', 1]}
print sorted(powers.items(), key=operator.itemgetter(0))

Hope this helps!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top