Notes on Python: The Dictionary as Control Flow
This is pretty much lifted straight from Mark Lutz’s Learning Python. It’s just a different way of implementing a simple if/elif/else
block akin to JavaScript’s switch/case
statement, or Clojure’s cond
.
So, normally in Python if you want a series of potential outcomes based on an input value then you implement a series of if/elif
statements…
In [2]: def sing_blues():
...: print "Choices:\nSkip James\nBessie Smith\nLeroy Carr\nVictoria Spivey\n"
...: choice = raw_input("Who would you like to hear today?\n")
...: if choice == "Skip James":
...: print "\nYou know, I'd rather be the ol' devil\n"
...: elif choice == "Bessie Smith":
...: print "\nI hate to see de evenin' sun go down\n"
...: elif choice == "Leroy Carr":
...: print "\nHow long babe how long\n"
...: elif choice == "Victoria Spivey":
...: print "\nDetroit's a cold, hard place, and I ain't got a dime to my name\n"
...: else:
...: print "\nNo Dice\n"
...:
In [3]: sing_blues()
Choices:
Skip James
Bessie Smith
Leroy Carr
Victoria Spivey
Who would you like to hear today?
Skip James
You know, I'd rather be the ol' devil
This multiway branching is pretty cumbersome and tedious, so alternatively we can use a dictionary to hold our choices, accessing it according to the input given…
In [4]: def sing_blues():
...: print "Choices:\nSkip James\nBessie Smith\nLeroy Carr\nVictoria Spivey\n"
...: choice = raw_input("Who would you like to hear today?\n")
...: print {"Skip James": "\nYou know, I'd rather be the ol' devil\n",
...: "Bessie Smith": "\nI hate to see de evenin' sun go down\n",
...: "Leroy Carr": "\nHow long babe how long\n",
...: "Victoria Spivey": "\nDetroit's a cold, hard place, and I ain't got a dime to my name\n"
....: }[choice]
...:
In [5]: sing_blues()
Choices:
Skip James
Bessie Smith
Leroy Carr
Victoria Spivey
Who would you like to hear today?
Victoria Spivey
Detroit's a cold, hard place, and I ain't got a dime to my name
Much more concise, eh? And to include a default else
clause…
In [13]: def sing_blues():
....: print "Choices:\nSkip James\nBessie Smith\nLeroy Carr\nVictoria Spivey\n"
....: choice = raw_input("Who would you like to hear today?\n")
....: choices = {"Skip James": "\nYou know, I'd rather be the ol' devil\n",
....: "Bessie Smith": "\nI hate to see de evenin' sun go down\n",
....: "Leroy Carr": "\nHow long babe how long\n",
....: "Victoria Spivey": "\nDetroit's a cold, hard place, and I ain't got a dime to my name\n"}
....: print choices.get(choice, "\nNo Dice\n")
....:
In [14]: sing_blues()
Choices:
Skip James
Bessie Smith
Leroy Carr
Victoria Spivey
Who would you like to hear today?
Bessie Smith
I hate to see de evenin' sun go down
In [15]: sing_blues()
Choices:
Skip James
Bessie Smith
Leroy Carr
Victoria Spivey
Who would you like to hear today?
Memphis Slim
No Dice
I like this, I think it’s a pretty neat way of describing potential branches, though I’m also aware that I’m constantly using newly discovered tools just because, like sticking lengthy list comprehensions
everywhere, rather than being more mature about these things and using tools appropriately. Mark suggests a rule of thumb to always err on the side of simplicity and readability, which I think is pretty sensible. Mark goes on to describe more complex branching beyond the simple key/value dictionary used here, using lambda functions
.
In [22]: def maths_on_two_numbers(action, a, b):
....: actions = {"+": (lambda a, b: a + b),
....: "-": (lambda a, b: a - b),
....: "*": (lambda a, b: a * b),
....: "/": (lambda a, b: a / b)}
....: print actions[action](a, b)
....:
In [23]: maths_on_two_numbers("+", 5, 7)
12
In [24]: maths_on_two_numbers("*", 5, 7)
35
Note how when we access the dictionary we have to pass in the arguments the lambda
expects. Creating the dictionary creates the lambda
functions, it doesn’t call them. That happens when we add the parenthesis to the dictionary access statement, which in turn requires the expected arguments. Without the parens we’d just get back the function…
In [16]: def maths_on_two_numbers(action, a, b):
....: actions = {"+": (lambda a, b: a + b),
....: "-": (lambda a, b: a - b),
....: "*": (lambda a, b: a * b),
....: "/": (lambda a, b: a / b)}
....: print actions[action]
....:
In [17]: maths_on_two_numbers("+", 5, 7)
<function <lambda> at 0x7f671d601848>
Well, this is just a brief note on something I found interesting, which I guess is what a blog post is. All credit due to Mark Lutz. I’m really enjoying going back over what I now know in Python and digging deeper, understanding properly what I’m doing and the more detailed nuances of the language.