[Tutor] KeyError

Danny Yoo dyoo at hkn.eecs.berkeley.edu
Mon Jun 14 18:33:25 EDT 2004



On Mon, 14 Jun 2004 Dragonfirebane at aol.com wrote:

> Using the command prompt to run my program, the following error occurs.
> I do not believe it is because I'm using the command prompt because the
> same error occurs in IDLE.  Since I used the same structure to convert
> text to binary as I did to convert text to hexadecimal (see code below
> error), I do not understand why the hexadecimal conversion doesn't work
> but the binary does.


Hi Dragonfirebane,

Ok, let's take a look.


> Additionally, much of the commenting had not been updated and is no
> longer correct, so please disregard any apparent misunderstanding
> through comments.


If the comments don't follow the code, drop the comments.

Incorrect comments do much more damage than good.  And comments that just
say "same deal" are, frankly speaking, not useful.  We can tell that it's
similar to the other conversion functions by just looking at them.



Here's converttxthex() with the comments stripped out:

###
def convertxthex():
    for char in original:
        x = 0
        if char in punct:
            res += char
        elif char in alphabet:
            while x <= 26:
                if char in alphabet[x]:
                    asHex = "%X" % int(number[x])
                    global res
                    for c in asHex:
                        res += hexadec[c]
                    if char not in punct:
                        res += ' '
                x += 1
            while 26 < x <= 52:
                if char in alphabet[x]:
                    asHex = "%X" % int(number[x - 26])
                    global res
                    for c in asHex:
                        res += hexadec[c]
                    if char not in punct:
                        res += ' '
                x += 1
###



Hmm... I'd strongly suggest reducing and simplifying the code here.  The
thing about long functions is that they're not fun to debug.  The thing
about highly nested functions is that they're hard to reason with.  Your
converttxthex is both long and deeply nested.  *grin*


Let's try shortening it first.  The bodies of both inner while loops look
almost exactly the same, so we can reduce the code by limiting the
conditional statement to the line that's varying,

###
asHex = "%X" % int(number[x - 26])
###



With this change, we can collapse those two while loops into one:

###
def convertxthex():
    for char in original:
        if char in punct:
            res += char
        elif char in alphabet:
            x = 0
            while x <= 52:
                if char in alphabet[x]:
                    if x <= 26:
                        asHex = "%X" % int(number[x])
                    else:
                        asHex = "%X" % int(number[x - 26])
                    global res
                    for c in asHex:
                        res += hexadec[c]
                    if char not in punct:
                        res += ' '
                x += 1
###


At the same time, I did a little bit of reorganizing: the initialization
of 'x=0' should be close to its usage as an indexing variable, right next
to the loop: it has nothing to do with what happens if the character is
punctuation, so that's why 'x=0' belongs in the 'elif char in alphabet'
body.


I still don't clearly understand the code's structure, since there's some
heavy nesting of the code.  I will break it out into some helper
functions:

###
def convertxthex():
    global res
    for char in original:
        if char in punct:
            res += char
        elif char in alphabet:
            convert_txt_hex_alphabet(char)


def convert_txt_hex_alphabet(char):
    global res
    x = 0
    while x <= 52:
        if char in alphabet[x]:
            if x <= 26:
                asHex = "%X" % int(number[x])
            else:
                asHex = "%X" % int(number[x - 26])
            for c in asHex:
                res += hexadec[c]
            if char not in punct:
                res += ' '
        x += 1
###


Ok, the structure is a little clearer here.  There are several things in
convert_txt_hex_alphabet_char() that look very fishy.  The 'hexadec'
dictionary is defined as:

###
hexadec =
{'0':'0',
 '1':'1',
 '2':'2',
 '3':'3',
 '4':'4',
 '5':'5',
 '6':'6',
 '7':'7',
 '8':'8',
 '9': '9',
 '10':'A',
 '11':'B',
 '12':'C',
 '13':'D',
 '14':'E',
 '15':'F'}
###


So conceptually, it's taking in a digit string, and returning the
corresponding hexadecimal value.  But 'asHex' here is defined as:

###
asHex = "%X" % int(number[x])
###

and this has no guarantee of being a digit string.  If we try asking
'hexadex' a key/value that it doesn't have, we'll get KeyError.


In fact, take a look at what the '%X' stuff is giving you back:

###
>>> for i in range(16):
...     print i, "%X" % i
...
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 A
11 B
12 C
13 D
14 E
15 F
###

"%X" already does hexadecimal conversion for you, so asHex is not
guaranteed to be a digit string.  That's the bug in the program.


The approach that you're taking now is very bug-prone.  I strongly
recommend you reconsider the program's approach here.

I'd also strongly suggest that you cut down the program's behavior to just
converting between a pair of types, and get that working nicely first.
Trying to add and extend the functionality of a buggy program is not a
good idea.


A lot of the program logic can dissolve if you use 'ord()' to convert from
a character to a particular number.  ord() is a built-in function, and is
described here:

    http://www.python.org/doc/lib/built-in-funcs.html#l2h-52


Good luck to you.




More information about the Tutor mailing list