0

I am studying Python3 tutorial on keyword arguments and couldn't reproduce the output due to the following code:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch 

What I got was a sorted dict:

----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

So I tried without calling cheeseshop():

>>> kw = {'shopkeeper':"Michael Palin", 'client':"John Cleese", 'sketch':"Cheese Shop Sketch"}
>>> kw
{'client': 'John Cleese', 'shopkeeper': 'Michael Palin', 'sketch': 'Cheese Shop Sketch'}

It looks like in version 3.5, the keys are automatically sorted. But in version 2.7, they aren't:

>>> kw
{'shopkeeper': 'Michael Palin', 'sketch': 'Cheese Shop Sketch', 'client': 'John Cleese'}

and I have to sort it in 2.7 to agree with 3.5.

>>> for k in sorted(kw):
...     print(k + " : " + kw[k])
... 
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

So the statement in the tutorial: "Note that the order in which the keyword arguments are printed is guaranteed to match the order in which they were provided in the function call." should be only applied to version 2.7, not 3.5. Is this true?

Leon Chang
  • 669
  • 8
  • 12
  • 8
    In 3.5, they’re in a random order. You should upgrade to current Python, 3.7, if you can, where order is preserved. (In 2.7, they’re also in a random order, but the order is consistent.) – Ry- Aug 22 '18 at 00:51
  • 1
    Usually, you shouldn't rely on dicts having any particular order. Occasionally, you need to—in that case, if you can't upgrade to Python 3.7, you have to explicitly use `OrderedDict` instead of `dict`. Relying on the arbitrary but repeatable order that pre-3.4 versions happened to have in some implementation on some platform is a bad idea. – abarnert Aug 22 '18 at 00:54

1 Answers1

0

Following the comments from @Ry and @abamert, I upgraded to Python 3.7, see link1 link2 and link3 for steps on how to build it from <3.7> source code. One note is that the Python default for Ubuntu 16.04 are versions 2.7 and 3.5 in /usr/bin. After the upgrade, 3.7 resides in /usr/local/bin. So we can have three versions coexistent.

As they said, do not rely on a particular version for the key order since the behavior of versions 2.7, 3.5 and 3.7 is different from each other:

  1. <2.7> order is random, but consistent/repeatable.
  2. <3.5> order is random and inconsistent.
  3. <3.7> order is preserved as entered and thus consistent.
  4. Use OrderedDict if you want it to be sorted. see below.

For example:

Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> kw = {'shopkeeper':"Michael Palin", 'client':"John Cleese", 'sketch':"Cheese Shop Sketch"}
>>> kw
{'client': 'John Cleese', 'shopkeeper': 'Michael Palin', 'sketch': 'Cheese Shop Sketch'}
>>>
>>> from collections import OrderedDict
>>> kws = OrderedDict(sorted(kw.items(), key = lambda x:x[0]))
>>> kws
OrderedDict([('client', 'John Cleese'), ('shopkeeper', 'Michael Palin'), ('sketch', 'Cheese Shop Sketch')])
>>> for i in kws:
...      print(i + " :\t" + kws[i])
...
client :        John Cleese
shopkeeper :    Michael Palin
sketch :        Cheese Shop Sketch
>>>
Leon Chang
  • 669
  • 8
  • 12