3

I have this bit of code that I need printed out in this exact order (Visitor Team, Visitor Rating, Home Team, Home Rating, Expected Winner, Margin) when I run it through tabulate.

final_dict = {'Visitor Team': visitor_team, 'Visitor Rating': visitor_rating, 'Home Team': home_team,
              'Home Rating': home_rating, 'Expected Winner': expected_winner, 'Margin': expected_winner_diff}

print(tabulate(final_dict, headers="keys", floatfmt=".2f", tablefmt="fancy_grid"))

I've been learning and using Python 3.6 and, unbeknownst to me, dicts in 3.6 are ordered now so this actually prints out as I intended it to. It was just dumb luck I guess that Python 3.6 gave me exactly what I needed!

But I went to install Python 3.5 on another computer and this doesn't print out like I want. I've been reading about ordereddicts but I'm not sure how exactly to use it. Do I need to declare final_dict as empty first and then iterate into it the key order I need?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
Biggen
  • 295
  • 4
  • 15
  • Dictionaries don't store the keys in order. You should use a list if you want to maintain the order. – Antimony Jun 03 '17 at 00:38
  • You didn't present the function `tabulate` but that's where the printing gets done. The important question here is, how does `tabulate` increment through the elements of `final_dict`? How does it use the value of `headers` to control the order of printing? How does it know which element of the dictionary matches which header? I think you should look at `tabulate` to understand how it works. Otherwise there is no guarantee that changing to an OrderedDict will fix your problem. – Paul Cornelius Jun 03 '17 at 01:03
  • @PaulCornelius Those are all good points. `tabulate` is a package used to printing tabular output: https://pypi.python.org/pypi/tabulate I didn't write it so I'm not exactly sure how it's 'internals' work. – Biggen Jun 03 '17 at 01:07
  • The docs https://pypi.python.org/pypi/tabulate say that the first argument to tabulate can be a list of lists. That seems to be the only sure-fire way to control the column order. Since that's important to you, I would convert your data to a list of lists before passing it off to tabulate. – Paul Cornelius Jun 03 '17 at 02:14
  • Relevant https://stackoverflow.com/questions/39980323/dictionaries-are-ordered-in-python-3-6 – Chris_Rands Jul 13 '17 at 07:56

1 Answers1

7

Dictionaries in Python 3.6 are ordered, but that feature is considered an implementation detail that you shouldn't rely upon (except in a few specific cases like **kwargs). If you do require a specific order, you should use collections.OrderedDict instead. You can construct one using a list of key, value tuples that are in the desired order:

from collections import OrderedDict

finaldict = OrderedDict([('Visitor Team', visitor_team),
                         ('Visitor Rating', visitor_rating),
                         ('Home Team', home_team),
                         ('Home Rating', home_rating),
                         ('Expected Winner', expected_winner),
                         ('Margin', expected_winner_diff),
                        ])

An OrderedDict works just like a normal dict in most respects, other than having a different repr and a few additional methods. You can read more about it in the docs.

In Python 3.6+ you'd also be able to use keyword arguments to the constructor if your key strings were valid identifiers (e.g. OrderedDict(Margin=expected_winner_diff)). Unlike the ordering of normal dicts, the order of keywords is guaranteed to be preserved (not an implementation detail). That's not backwards compatible though (and can't work for your non-identifier keys anyway).

But it's probably worth considering that if you need a very specific order for your data, a dictionary may not be the best type to use to store it in. I see the tabulate function you're using comes from a library, and according to the docs, it accepts many different formats of data. I'd probably just pass it a list of column data, and give it the headers separately:

data = [visitor_team, visitor_rating, home_team,
        home_rating, expected_winner, expected_winner_diff]

headers = ["Visitor Team", "Visitor Rating", "Home Team",
           "Home Rating", "Expected Winner", "Margin"]

print(tabulate(data, headers=headers, floatfmt=".2f", tablefmt="fancy_grid"))

(Note, I've not actually tested that code, since I don't have the tabulate library on my system. But it should at least be close to working.)

Blckknght
  • 100,903
  • 11
  • 120
  • 169
  • The docs say that `data` has to be a two-dimensional data type, like a list of lists or a dict of lists. A simple list won't work. I'm guessing that OP's original code works because all of his dictionary values are actually lists. – Paul Cornelius Jun 03 '17 at 02:18
  • @PaulCornelius Yes, that is correct. All of that is stored as lists. I'll play around with what I have and see what I can come up with. – Biggen Jun 03 '17 at 02:26
  • I ended up just converting my finaldict to a list. I figured it was a whole lot easier just dealing with lists and not messing around with getting ordereddict to work. Thanks for your help guys! – Biggen Jun 03 '17 at 18:15