[Tutor] A problem with the shelve module

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Sep 6 02:47:24 CEST 2004



On Sun, 5 Sep 2004, Kent Johnson wrote:

> Every time you call a function, it gets a new set of local variables.
> This is true even if you are calling the same function recursively. So
> every time you call Address(), you make a new book object. The book that
> you are using to lookup MarkK is not the same book that you added MarkK
> to because it is in a different invocation of Address()! So the lookup
> fails with a KeyError.

Hi Kent,

Yes, the scope of the book creation is in the wrong place: it needs to be
outside of the loop, not within.


The 'book' is scheduled to be flushed to disk when the Address() function
exits.  But by using recursion to reenter Address(), we don't give Python
the chance to close the shelved dictionary.


The wrong way to fix this, by the way, is to call close right before every
recursive call:

         ICQ=raw_input("ICQ number(type 'none' if you want add ICQ later): ")
         book[addname]=(addname,phone,Addressnum,ICQ)
         book.close()
         Address()
     elif Achoice=="2":
         namecheck=raw_input("Enter the name you want to view: ")
         book[namecheck]
         book.close()
         Address()

Although this will attack the symptom, this approach is probably a bad
fix.  The repetitive nature of the fix is hint enough that we may want to
really go after the root cause: 'book' should be defined outside of the
recursion.


Mark, if you're uncomfortable about using globals, and if you still want
to preserve the recursive structure of your code, you can use an inner
function definition.  Something like this:

###
def Address():
    book=shelve.open("Address book")

    def loop():
        print_choices()
        Achoice = raw_input()
        if Achoice == '3':
            ...
        if Achoice == '2':
            ...
        ...
    loop()
###

should also work.  'book' is visible within the loop() because of "lexical
scoping".


Mark's coding style feels a bit like Scheme, so he may understand this
better than loops.  But this structure can be easily transformed into a
while loop:

###
def Address():
    book=shelve.open("Address book")

    while True:
        print_choices()
        Achoice = raw_input()
        if Achoice == '3':
            ...
        if Achoice == '2':
            ...
        ...
###

and since we're programming in Python, I'd strongly recommending using an
explicit loop here.


> PS A question for the other tutors - does this recursive style of user
> input make you nervous?

I'm actually used to seeing this style: it is the style used by functional
programmers to do loop iteration.  In the Scheme or OCaml languages, it
works perfectly well --- if we were tutoring those languages, I wouldn't
have problems with it.

Unfortunately, it doesn't work so well in Python because Python doesn't do
tail call elimination.  So I think we should encourage newcomers here to
use loops in this particular kind of straightforward, "iterative"
recursion.

The majority of Python programmers are comfortable with loops:  I think we
should help new programmers write programs in a way that are easy for the
Python community to read.


> I don't much like it but I don't know if it is just because it is
> unfamiliar...my gut feeling is you could get in trouble with it.

One Python-specific problem has to do with Python's debugging facilities,
in particular, the stack trace.  The stack trace gets can get really huge
if we use recursion to implement iteration, and this can make debugging
recursive programs messy:

> >   File "C:\ab.py", line 57, in ?
> >     startmenu()
> >   File "C:\ab.py", line 45, in startmenu
> >     Address()
> >   File "C:\ab.py", line 28, in Address
> >     Address()
> >   File "C:\ab.py", line 28, in Address
> >     Address()
> >   File "C:\ab.py", line 32, in Address
> >     Address()
> >   File "C:\ab.py", line 28, in Address
> >     Address()
> >   File "C:\ab.py", line 31, in Address
> >     book[namecheck]

Since we're in Python-tutor, I think we should encourage folks to use
loops, if they can be obviously used: it'll make our own lives easier in
reading tracebacks.  *grin*



More information about the Tutor mailing list