Python Using Databases in Python Our Diary App Switching It Up

Pete Webb
Pete Webb
7,635 Points

I don't understand the relationship between the menu OrderDict tuple, choice assignment, and the if choice in menu bloc

In the video Kenneth kindly goes through the the while loop inside the menu_loop() function partly as shown here

from peewee import *
from collections import OrderedDict

def menu_loop():
    """Show the menu"""
    choice = None

    while choice != 'q':
        print("Enter 'q' to quit.")
        for key, value in menu.items():
            print('{}) {}'.format(key, value.__doc__))
        choice = input('Action: ').lower().strip() 

        if choice in menu:
            menu[choice]()


def add_entry():
    """Add an entry."""


def view_entries():
    """View previous entries."""


menu = OrderedDict([
        ('a', add_entry),
        ('v', view_entries),
    ])

Let's say we have choice = 'a'. Fine 'a' is in menu, so if-block runs. Fine menu[choice]() somehow executes add_entry() ?

I must have missed something with tuples. I don't understand how menu['a'] equates to add_entry.

On another note. What's with the parens after menu[choice]? I get that it executes the function, but why is it there and not in the menu? As in

menu = OrderedDict([
        ('a', add_entry()),
        ('v', view_entries()),
    ])

Would this work if you just call menu[choice]?

Any help would be much appreciated!

2 Answers

Hi Pete

I took this module a several weeks ago I think and re-watched it now. This is how I understand all of it.

This thing here value.__doc__ prints out the docstring of a value. In our case the values are actually functions. The docstring for add_entry is """Add an entry.""".

In this next block we have the choices where, for example, 'a' is the key and the corresponding value is add_entry which is actually a function as mentioned before. In ordered dictionaries we add things as a list. In regular dictionaries we would use a colon instead of a comma like ('a': add_entry).

menu = OrderedDict([
        ('a', add_entry),
        ('v', view_entries),
    ])

You can loop through the keys and values in the ordered dictionary just like in a regular dictionary. Here's an example for you:

menu = OrderedDict([
        ('a', 'add_entry'),
        ('v', 'view_entries'),
    ])

for key in menu.keys():
    print(key)

for value in menu.values():
    print(value)

for key, value in menu.items():
    print(f"The key is '{key}' and the value is '{value}'.")

The output for the above is:

a
v
add_entry
view_entries
The key is 'a' and the value is 'add_entry'.
The key is 'v' and the value is 'view_entries'.

So the items in menu.items(): are the keys and values in the variable menu.

I played around with the code in PyCharm and check this - I changed the docstring for this function def add_entry()::

from peewee import *
from collections import OrderedDict

def menu_loop():
    """Show the menu"""
    choice = None

    while choice != 'q':
        print("Enter 'q' to quit.")
        for key, value in menu.items():
            print('{}) {}'.format(key, value.__doc__))
        choice = input('Action: ').lower().strip() 

        if choice in menu:
            menu[choice]()


def add_entry():
    """Just a test."""


def view_entries():
    """View previous entries."""


menu = OrderedDict([
        ('a', add_entry),
        ('v', view_entries),
    ])

menu_loop()

The output for the above is:

Enter 'q' to quit.
a) Just a test.
v) View previous entries.
Action: 

If you lose the parens after menu[choice] the statement will have no effect. For example, menu[a] is the function add_entry (as we can see from the menu) and we need to run it so this is why we need to use the parens here. So it's like menu[a]() is equal to add_entry().

And we don't want parens here ('a', add_entry()), since we do not want to call the function. If we used parens then the output would be:

Enter 'q' to quit.
a) None
v) View previous entries.
Action: 

So a) would be None since we are calling the function add_entry and there is nothing to return. The function add_entry has only a docstring and nothing else.

I am not entirely sure if this all helps. It's a bit difficult to explain in writing.

Pete Webb
Pete Webb
7,635 Points

Thanks Ave, this is really helpful

I did not realise OrderedDict allows us to use a list of tuples as a dictionary. I panicked thinking I must have missed something about tuples since, as far as I understand, you don't have key: value pairs. Thank you for clarifying that

Yes I think I understand the difference between having menu[choice]() and menu[choice] with the parens on the functions in the menu variable now. Like you say we don't want to call the function from the menu, but we do want to get the docstring from it. Doing

for key, value in menu.items():
    print "{}) {}".format(key, value.__doc__)

menu = OrderedDict([
    ('a', add_entry()),
    ('v', view_entry()),
])

Won't grab the docstring, but attempt to run the function in menu instead. Which, like you say, returns None since the add_entry()function currently does not return anything. Using menu[choice]() allows us to run the function dynamically.

Thank you so much

Hi Pete

I'm so happy that I managed to clarify this and help you! When I wrote the text down here in the community forum I read it like a thousand times before posting to make sure it's as clear as possible. It seemed a bit of a tricky topic when I first learned about this myself so I'm happy to help!

Thank you very much for the positive feedback! :-)

Pete Webb
Pete Webb
7,635 Points

Haha I know the feeling; "does this make sense?" "am I explaining this right?"

I thought your explanation was spot on

No worries, thanks again for the help. Much more comfortable with this now