< BACKMake Note | BookmarkCONTINUE >
156135250194107072078175030179198180024228156016206217188240240204175203013157019136225165

Dictionary Keys

Dictionary values have no restrictions. They can be any arbitrary Python object, i.e., from standard objects to user-defined objects. However, the same cannot be said of keys.

More Than One Entry per Key Not Allowed

One rule is that you are constrained to having only one entry per key. In other words, multiple values per the same key are not allowed. (Container objects such as lists, tuples, and other dictionaries are fine.) When key collisions are detected (meaning duplicate keys encountered during assignment), the last assignment wins.

						
>>> dict1 = {' foo':789, 'foo': 'xyz'}
>>> dict1
{'foo': 'xyz'}
>>>
>>> dict1['foo'] = 123
>>> dict1
{'foo': 123}

					

Rather than producing an error, Python does not check for key collisions because that would involve taking up memory for each key-value pair assigned. In the above example where the key 'foo' is given twice on the same line, Python applies the key-value pairs from left to right. The value 789 may have been set at first, but is quickly replaced by the string 'xyz'. When assigning a value to a non-existent key, the key is created for the dictionary and value added, but if the key does exist (a collision), then its current value is replaced. In the above example, the value for the key 'foo' is replaced twice; in the final assignment, 'xyz' is replaced by 123.

Keys Must Be Immutable

As we mentioned earlier in Section 7.1, most Python objects can serve as keys—only mutable types such as lists and dictionaries are disallowed. In other words, types that compare by value rather than by identity cannot be used as dictionary keys. A TypeError will occur if a mutable type is given as the key:

						
>>> dict[[3]] = 14
Traceback (innermost last):
  File "<stdin>," line 1, in ?
TypeError: unhashable type

					

Why must keys be immutable? The hash function used by the interpreter to calculate where to store your data is based on the value of your key. If the key was a mutable object, its value could be changed. If a key changes, the hash function will map to a different place to store the data. If that was the case, then the hash function could never reliably store or retrieve the associated value. Immutable keys were chosen for the very fact that their values cannot change. (Also see the Python FAQ question 6.18.)

We know that numbers and strings are allowed as keys, but what about tuples? We know they are immutable, but in Section 6.17.2, we hinted that they might not be as immutable as they can be. The clearest example of that was when we modified a list object which was one of our tuple elements. To allow tuples as valid keys, one more restriction must be enacted: Tuples are valid keys only if they only contain immutable arguments like numbers and strings.

We conclude this chapter on dictionaries by presenting a program (userpw.py as in Example 7.1), which manages user name and passwords in a mock login entry database system. This script accepts new users given that they provide a login name and a password. Once an "account" has been set up, an existing user can return as long as they give their login and correct password. New users cannot create an entry with an existing login name.

Example 7.1. Dictionary Example (userpw.py)

This application manages a set of users who join the system with a login name and a password. Once established, existing users can return as long as they remember their login and password. New users cannot create an entry with someone else's login name.

 <$nopage>
001 1  #!/usr/bin/env python
002 2
003 3  db = {}
004 4
005 5  def newuser():
006 6      prompt = 'login desired: '
007 7      while 1:
008 8          name = raw_input(prompt)
009 9          if db.has_key(name):
010 10             prompt = 'name taken, try another: '
011 11             continue <$nopage>
012 12         else: <$nopage>
013 13             break <$nopage>
014 14     pwd = raw_input('passwd: ')
015 15     db[name] = pwd
016 16
017 17 def olduser():
018 18     name = raw_input('login: ')
019 19     pwd = raw_input('passwd: ')
020 20     passwd = db.get(name)
021 21     if passwd == pwd:
022 22         pass <$nopage>
023 23     else: <$nopage>
024 24         print 'login incorrect'
025 25         return <$nopage>
026 26
027 27     print 'welcome back', name
028 28
029 29 def showmenu():
030 30     prompt = """
031 31 (N)ew User Login
032 32 (E)xisting User Login
033 33 (Q)uit
034 34
035 35 Enter choice: """
036 36
037 37     done = 0
038 38     while not done:
039 39
040 40         chosen  = 0
041 41         while not chosen:
042 42             try: <$nopage>
043 43                 choice = raw_input(prompt)[0]
044 44             except (EOFError, KeyboardInterrupt):
045 45                 choice = 'q'
046 46             print '\nYou picked: [%s]' % choice
047 47             if choice not in 'neq':
048 48                 print 'invalid option, try again'
049 49             else: <$nopage>
050 50                 chosen = 1
051 51
052 52         if choice == 'q': done = 1
053 53         if choice == 'n': newuser()
054 54         if choice == 'e': olduser()
055 55
056 56 if __name__ == '__main__':
057 57     showmenu()
058  <$nopage>
Lines 1 – 3

After the UNIX-startup line, we initialize the program with an empty user database. Because we are not storing the data anywhere, a new user database is created every time this program is executed.

Lines 5 – 15

The newuser() function is the code that serves new users. It checks to see if a name has already been taken, and once a new name is verified, the user is prompted for his or her password (no encryption exists in our simple program), and his or her password is stored in the dictionary with his or her user name as the key.

Lines 17 – 27

The olduser() function handles returning users. If a user returns with the correct login and password, a welcome message is issued. Otherwise, the user is notified of an invalid login and returned to the menu. We do not want an infinite loop here to prompt for the correct password because the user may have inadvertently entered the incorrect menu option.

Lines 29 – 54

The real controller of this script is the showmenu() function. The user is presented with a friendly menu. The prompt string is given using triple quotes because it takes place over multiple lines and is easier to manage on multiple lines than on a single line with embedded '\n' symbols. Once the menu is displayed, it waits for valid input from the user and chooses which mode of operation to follow based on the menu choice. The try-except statements we describe here are the same as for the stack.py and queue.py examples from the last chapter (see Section 6.14.1).

Lines 56 – 57

Here is the familiar code which will only call showmenu() to start the application if the script was involved directly (not imported).

Here is a sample execution of our script:

							
% userpw.py

(N)ew User Login
(E)xisting User Login
(Q)uit

Enter choice: n

You picked: [n]
login desired: king arthur
passwd: grail

(N)ew User Login
(E)xisting User Login
(Q)uit

Enter choice: e

You picked: [e]
login: sir knight
passwd: flesh wound
login incorrect

(N)ew User Login
(E)xisting User Login
(Q)uit

Enter choice: e

You picked: [e]
login: king arthur
passwd: grail
welcome back king arthur

(N)ew User Login
(E)xisting User Login
(Q)uit

Enter choice: ^D
You picked: [q]

						


Last updated on 9/14/2001
Core Python Programming, © 2002 Prentice Hall PTR

< BACKMake Note | BookmarkCONTINUE >

© 2002, O'Reilly & Associates, Inc.