Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #51114 > unrolled thread

Python 3: dict & dict.keys()

Started byEthan Furman <ethan@stoneleaf.us>
First post2013-07-23 18:16 -0700
Last post2013-07-25 12:20 +1000
Articles 6 — 4 participants

Back to article view | Back to comp.lang.python


Contents

  Python 3: dict & dict.keys() Ethan Furman <ethan@stoneleaf.us> - 2013-07-23 18:16 -0700
    Re: Python 3: dict & dict.keys() Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-07-24 02:11 +0000
      Re: Python 3: dict & dict.keys() Ian Kelly <ian.g.kelly@gmail.com> - 2013-07-24 09:02 -0600
      Re: Python 3: dict & dict.keys() Ethan Furman <ethan@stoneleaf.us> - 2013-07-24 17:59 -0700
        Re: Python 3: dict & dict.keys() Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2013-07-25 05:57 +0000
      Re: Python 3: dict & dict.keys() Ben Finney <ben+python@benfinney.id.au> - 2013-07-25 12:20 +1000

#51114 — Python 3: dict & dict.keys()

FromEthan Furman <ethan@stoneleaf.us>
Date2013-07-23 18:16 -0700
SubjectPython 3: dict & dict.keys()
Message-ID<mailman.5024.1374628577.3114.python-list@python.org>
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.

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


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'


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?

--
~Ethan~

[toc] | [next] | [standalone]


#51116

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-07-24 02:11 +0000
Message-ID<51ef37e3$0$29971$c3e8da3$5496439d@news.astraweb.com>
In reply to#51114
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

[toc] | [prev] | [next] | [standalone]


#51140

FromIan Kelly <ian.g.kelly@gmail.com>
Date2013-07-24 09:02 -0600
Message-ID<mailman.5042.1374678199.3114.python-list@python.org>
In reply to#51116
On Tue, Jul 23, 2013 at 8:11 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> Basically, views are set-like, not list-like.

The keys and items views are set-like.  The values view is not.

[toc] | [prev] | [next] | [standalone]


#51179

FromEthan Furman <ethan@stoneleaf.us>
Date2013-07-24 17:59 -0700
Message-ID<mailman.5075.1374713993.3114.python-list@python.org>
In reply to#51116
On 07/23/2013 07:11 PM, Steven D'Aprano wrote:
> On Tue, 23 Jul 2013 18:16:08 -0700, Ethan Furman wrote:
>>
>> 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.

Poetic effect.  Dramatic license.  Blah blah.  ;)


> 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())."

Actually, I would recommend `list(d)`, which also works the same in both 2 and 3.

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#51191

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2013-07-25 05:57 +0000
Message-ID<51f0be51$0$29971$c3e8da3$5496439d@news.astraweb.com>
In reply to#51179
On Wed, 24 Jul 2013 17:59:43 -0700, Ethan Furman wrote:

>> 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())."
> 
> Actually, I would recommend `list(d)`, which also works the same in both
> 2 and 3.

Fair point.



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#51183

FromBen Finney <ben+python@benfinney.id.au>
Date2013-07-25 12:20 +1000
Message-ID<mailman.5077.1374719106.3114.python-list@python.org>
In reply to#51116
Ethan Furman <ethan@stoneleaf.us> writes:

> On 07/23/2013 07:11 PM, Steven D'Aprano wrote:
> > On Tue, 23 Jul 2013 18:16:08 -0700, Ethan Furman wrote:
> >> 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.
>
> Poetic effect.  Dramatic license.  Blah blah.  ;)

Text-only medium. Clarity of communication. Et cetera. :-)

-- 
 \           “Two hands working can do more than a thousand clasped in |
  `\                                               prayer.” —Anonymous |
_o__)                                                                  |
Ben Finney

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web