0

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

Community
  • 1
  • 1
juggler
  • 319
  • 1
  • 5
  • 16
  • 6
    Dictionaries are hashmaps and not ordered. If you need it ordered, you can use `OrderedDict` from collections. – Hyperboreus Dec 12 '13 at 19:32
  • Also, your current `AutoVivification` class is essentially a nested `defaultdict`, as in `dd = lambda: defaultdict(dd)`... – l4mpi Dec 12 '13 at 19:40
  • All the stuff about auto vivification is irrelevant to the question. Just do `print {'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1}` and you'll see the values reordered in the exact same way. – abarnert Dec 12 '13 at 19:40
  • And that means this is a dup of about 300 other questions on SO… I'll pick one of them at random. – abarnert Dec 12 '13 at 19:45
  • @abarnert not the best choice as the accepted answer uses django `SortedDict`s (the second answer uses `OrderedDict` though)... but well whatever, this question's already exhaustively answered anyways. – l4mpi Dec 12 '13 at 19:50
  • As a side note, your code isn't taking advantage of autovivication at all. `powers[1] = { … }` works fine with a plain order `dict`. It's only if you want to be able to write `powers['spam']['eggs'] = 'spam'` (without first creating `powers['spam']`) that you need autovivification. – abarnert Dec 12 '13 at 20:00
  • @l4mpi: You're right. Well, you can pick another of the 300, less carelessly than me, and close as a dup of that. :) – abarnert Dec 12 '13 at 20:01
  • @abarnert sadly I was a bit too fast and only checked the answers after closevoting for your choice :) Anyways, doesn't make a huge difference now that there's an excellent answer from you below. – l4mpi Dec 12 '13 at 20:07
  • and yes, I did check SO for where the answer might already be provided. I guess I just lack experience choosing the right search terms, or didn't look for long enough. – juggler Dec 12 '13 at 22:32
  • and thank's for the note with spam and eggs, perhaps I don't need the autovivi thing at all.. – juggler Dec 12 '13 at 22:33
  • @abarnert sorry about that, see my comments above.. – juggler Dec 12 '13 at 22:42

3 Answers3

3

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.)

Community
  • 1
  • 1
abarnert
  • 354,177
  • 51
  • 601
  • 671
  • tried this, still get the same thing. changed my code to:`powers = OrderedAutoVivification()`, used your code: `def OrderedAutoVivification(): return OrderedDefaultDict(AutoVivification)`, and used the `OrderedDefaultDict` from [link](http://stackoverflow.com/questions/4126348/how-do-i-rewrite-this-function-to-implement-ordereddict/4127426#4127426). I'll keep looking.. – juggler Dec 12 '13 at 20:58
  • I should maybe submit another question with the new code. hm. I'm concluding this isn't simple.. (obviously?) – juggler Dec 12 '13 at 21:08
  • @juggler: Did you read my entire answer? I explained why you will get the exact same thing: `{'c1': 0.5, 'gamma': 1, 'lambda': 1, 'A': 1}` is a `dict`, which means it has an arbitrary order. Assigning that arbitrarily-ordered `dict` to `powers[1]` is just going to lock in that original arbitrary order as the permanent order. You need to avoid constructing a `dict` in the first place, by using a sequence of pairs (or by adding the values one by one, or in some other way). – abarnert Dec 12 '13 at 21:28
  • I did read the whole thing. didn't understand all of it :-) I did gradually figure out (trying many of the suggestions of others as well)... basically, my goal isn't to simultaneously invoke both autovivification and ordered dictionaries. For now, that's a whole other question that quite possibly you have answered. what my code above is actually trying to do is create a dictionary of dictionaries, and it's the sub-dictionary that I'm trying to print. so this code works:`powers = AutoVivification() powers[1] = OrderedDict([('c1', 0.5), ('gamma', 1), ('lambda', 1), ('A', 1)])`. :-) – juggler Dec 12 '13 at 22:20
  • @juggler: Yes, that will do what you want. – abarnert Dec 12 '13 at 23:13
-1

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]
Adam Smith
  • 52,157
  • 12
  • 73
  • 112
  • The reference to "sortable" here is confusing; I'm not sure what you meant to say. – abarnert Dec 12 '13 at 19:39
  • will using OrderedDict() have the same effect as AutoVivification(), as well as ordering it? (I'm still fuzzy on autovivification. all I really know is that it makes arbitrarilly expanding the dictionary super easy, which is great) – juggler Dec 12 '13 at 19:41
  • @MichaelJensen: The easy way to do autovivification is with `defaultdict`, as l4mpi's comment explains. If you want it ordered _and_ auto-vivified, you can use an `OrderedDefaultDict` (search for a recipe for it if you don't know how to build it yourself) the exact same way. – abarnert Dec 12 '13 at 19:43
  • @abarnert I clarified my answer, thanks. I don't know what AutoVivification() is either! :) – Adam Smith Dec 12 '13 at 19:46
  • OK now that I know what you mean by "sorted", I fail to see how it's relevant. He's never sorting the dict, and neither are you. And the important part is that it won't retain its order even if you do _nothing_ to it. – abarnert Dec 12 '13 at 19:47
  • hm. this solution doesn't actually work for me. I get exactly the same thing. – juggler Dec 12 '13 at 19:55
  • Yes, this answer makes `powers` preserve its order, but if any of its values are dicts, they won't preserve _their_ orders. Plus, even if it did magically do that somehow, it wouldn't help, because you're constructing a `dict` to assign to `powers[1]`, so by the time the assignment happens, the ordering is already arbitrary. – abarnert Dec 12 '13 at 19:58
-4

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!

Andrew Kloos
  • 4,189
  • 4
  • 28
  • 36
  • Answer to wrong question? – Hyperboreus Dec 12 '13 at 19:37
  • How is that? DropDownListValues is a Dict. – Andrew Kloos Dec 12 '13 at 19:38
  • 1. That's about the worst way to order a dict and 2. This question has nothing to do with jinja or html. – l4mpi Dec 12 '13 at 19:38
  • 3. Installing jinja2 for ordering dictionaries is somewhat senseless. – Hyperboreus Dec 12 '13 at 19:42
  • @Hyperboreus I believe that's just meant as an example (or quickly copy-pasted already existing code) - the actual ordering process doesn't have anything to do with jinja. But IMO it's a bad example as it's way too verbose, badly formatted and contains mostly unrelated code. – l4mpi Dec 12 '13 at 19:44
  • what's wrong with a proof of concept? I refined my code to be more simple... – Andrew Kloos Dec 12 '13 at 19:49
  • The result of your current code is simple as well - `SyntaxError`. An example is wrong if most code is unrelated to the problem; that's made worse by bad formatting and no explanations for the code. While the formatting is now much better, the solution itself would be bad even without the syntax errors (and disregarding `OrderedDict`). A few hundered chars are not enough to accurately describe why, but in essence, you're mixing data (values) and structure (ordering), modifying the original structure, need to keep track of indices yourself, and couldn't easily fit OPs Autovivification usecase. – l4mpi Dec 12 '13 at 20:04
  • I see what you are saying. This solution would only work if the dict is appended to not inserted into. – Andrew Kloos Dec 12 '13 at 20:27