See All Titles |
![]() ![]() Dictionary KeysDictionary 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 AllowedOne 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 ImmutableAs 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 – 3After 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 – 15The 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 – 27The 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 – 54The 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 – 57Here 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]
|
© 2002, O'Reilly & Associates, Inc. |