On Tue, 23 Jul 2013 18:16:08 -0700, Ethan Furman wrote: > Back in Python 2.x days I had a good grip on dict and dict.keys(), and > when to use one or the other. > > Then Python 3 came on the scene with these things called 'views', and > while range couldn't be bothered, dict jumped up and down shouting, "I > want some!" > > So now, in Python 3, .keys(), .values(), even .items() all return these > 'view' thingies. > > And everything I thought I knew about when to use one or the other went > out the window.
Surely not. The fundamental behaviour of Python's data model hasn't changed. Lists are lists, views are views, and iterators are iterators. Only the way you get each has changed. - If in Python 2, you used the viewkeys() method, that's been renamed keys() in Python 3. So d.viewkeys() => d.keys(). - If in Python 2, you used the keys() method, it returns a list, and like any function that has been made lazy instead of eager in Python 3 (e.g. map, zip, filter) if you want the same behaviour, simply call list manually. So d.keys() => list(d.keys()). - If in Python 2, you used the iterkeys() methods, it returns a simple iterator, not a view. So d.iterkeys() => iter(d.keys()). None of these distinctions really matter if all you are doing is iterating over the keys, without modifying the dict. Not in Python 2, nor in Python 3. And naturally the same applies to the various flavours of *items and *values. > For example, if you need to modify a dict while iterating over it, use > .keys(), right? Wrong: > > --> d = {1: 'one', 2:'two', 3:'three'} --> for k in d.keys(): > ... if k == 1: > ... del d[k] > ... > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > RuntimeError: dictionary changed size during iteration Fundamentally, this behaviour has not changed from Python 2: you should not iterate over a data structure while changing it, instead you should make a copy of the data you iterate over. In Python 2, d.keys() makes a copy and returns a list, so in Python 3 you would call list(d.keys()). > If you need to manipulate the keys (maybe adding some, maybe deleting > some) before doing something else with final key collection, use > .keys(), right? Wrong: > > --> dk = d.keys() > --> dk.remove(2) > Traceback (most recent call last): > File "<stdin>", line 1, in <module> > AttributeError: 'dict_keys' object has no attribute 'remove' Repeat after me: "In Python 2, d.keys() returns a list of keys, so if I want a list of keys in Python 3, call list explicitly list(d.keys())." > I understand that the appropriate incantation in Python 3 is: > > --> for k in list(d) > ... ... > > or > > --> dk = list(d) > --> dk.remove(2) > > which also has the added benefit of working the same way in Python 2. > > So, my question boils down to: in Python 3 how is dict.keys() different > from dict? What are the use cases? *shrug* For most purposes, there is no difference, especially when merely iterating over the dict. Such differences as exist are trivial: - if you need an actual callable function or method, say to pass to some other function, you can do this: for method in (d.items, d.keys, d.values): process(method) instead of this: # untested for method in (d.items, d.keys, lambda d=d: iter(d)): process(method) - d.keys() is a view, not the dict itself. That's a pretty fundamental difference: compare dir(d.keys()) with dir(d). Basically, views are set-like, not list-like. -- Steven -- http://mail.python.org/mailman/listinfo/python-list