Learning Python - Mark Lutz [138]
Here is what keys lists look like when used in set operations. In set operations, views may be mixed with other views, sets, and dictionaries (dictionaries are treated the same as their keys views in this context):
>>> K | {'x': 4} # Keys (and some items) views are set-like
{'a', 'x', 'c'}
>>> V & {'x': 4}
TypeError: unsupported operand type(s) for &: 'dict_values' and 'dict'
>>> V & {'x': 4}.values()
TypeError: unsupported operand type(s) for &: 'dict_values' and 'dict_values'
>>> D = {'a':1, 'b':2, 'c':3}
>>> D.keys() & D.keys() # Intersect keys views
{'a', 'c', 'b'}
>>> D.keys() & {'b'} # Intersect keys and set
{'b'}
>>> D.keys() & {'b': 1} # Intersect keys and dict
{'b'}
>>> D.keys() | {'b', 'c', 'd'} # Union keys and set
{'a', 'c', 'b', 'd'}
Dictionary items views are set-like too if they are hashable—that is, if they contain only immutable objects:
>>> D = {'a': 1}
>>> list(D.items()) # Items set-like if hashable
[('a', 1)]
>>> D.items() | D.keys() # Union view and view
{('a', 1), 'a'}
>>> D.items() | D # dict treated same as its keys
{('a', 1), 'a'}
>>> D.items() | {('c', 3), ('d', 4)} # Set of key/value pairs
{('a', 1), ('d', 4), ('c', 3)}
>>> dict(D.items() | {('c', 3), ('d', 4)}) # dict accepts iterable sets too
{'a': 1, 'c': 3, 'd': 4}
For more details on set operations in general, see Chapter 5. Now, let’s look at three other quick coding notes for 3.0 dictionaries.
Sorting dictionary keys
First of all, because keys does not return a list, the traditional coding pattern for scanning a dictionary by sorted keys in 2.X won’t work in 3.0. You must either convert to a list manually or use the sorted call introduced in Chapter 4 and earlier in this chapter on either a keys view or the dictionary itself:
>>> D = {'a':1, 'b':2, 'c':3}
>>> D
{'a': 1, 'c': 3, 'b': 2}
>>> Ks = D.keys() # Sorting a view object doesn't work!
>>> Ks.sort()
AttributeError: 'dict_keys' object has no attribute 'sort'
>>> Ks = list(Ks) # Force it to be a list and then sort
>>> Ks.sort()
>>> for k in Ks: print(k, D[k])
...
a 1
b 2
c 3
>>> D
{'a': 1, 'c': 3, 'b': 2}
>>> Ks = D.keys() # Or you can use sorted() on the keys
>>> for k in sorted(Ks): print(k, D[k]) # sorted() accepts any iterable
... # sorted() returns its result
a 1
b 2
c 3
>>> D
{'a': 1, 'c': 3, 'b': 2} # Better yet, sort the dict directly
>>> for k in sorted(D): print(k, D[k]) # dict iterators return keys
...
a 1
b 2
c 3
Dictionary magnitude comparisons no longer work
Secondly, while in Python 2.6 dictionaries may be compared for relative magnitude directly with <, >, and so on, in Python 3.0 this no longer works. However, it can be simulated by comparing sorted keys lists manually:
sorted(D1.items()) < sorted(D2.items()) # Like 2.6 D1 < D2
Dictionary equality tests still work in 3.0, though. Since we’ll revisit this in the next chapter in the context of comparisons at large, we’ll defer further details here.
The has_key method is dead: long live in!
Finally, the widely used dictionary has_key key presence test method is gone in 3.0. Instead, use the in membership expression, or a get with a default test (of these, in is generally preferred):
>>> D
{'a': 1, 'c': 3, 'b': 2}
>>> D.has_key('c') # 2.X only: True/False
AttributeError: 'dict' object has no attribute 'has_key'
>>> 'c' in D
True
>>> 'x' in D
False
>>> if 'c' in D: print('present', D['c']) # Preferred in 3.0
...
present 3
>>> print(D.get('c'))
3
>>> print(D.get('x'))
None
>>> if D.get('c') != None: print('present', D['c']) # Another option
...
present 3
If you work in 2.6 and care about 3.0 compatibility, note that the first two changes (comprehensions and views) can only be coded in 3.0, but the last three (sorted, manual comparisons, and in) can be coded in 2.6 today to ease 3.0 migration