This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: dict() in dict(foo='bar').keys() raises
Type: Stage:
Components: Versions: Python 3.2
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: dwt, rhettinger, vstinner
Priority: normal Keywords:

Created on 2014-01-08 13:35 by dwt, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (6)
msg207683 - (view) Author: Martin Häcker (dwt) Date: 2014-01-08 13:34
I was quite surprised by this behavior:

>>> dict() in [dict()]
True
>>> dict() in []
False
>>> dict() in dict(foo='bar').keys()
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>> dict() in list(dict(foo='bar').keys())
False

I think it should change. Calling dict.keys() one expects to get list like behavior and not having to ensure that everything that is checked for inclusion there has to be hasheable.

If it helps, this is also a regression from python 2.6/7 where this works as expected.
msg207684 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-01-08 13:38
"key in dict" checks if dict has the specified key. Dictionary keys must be hashable. Dictionaries are not hashable.

> If it helps, this is also a regression from python 2.6/7 where this works as expected.

dict.keys() return a list in Python 2, whereas it returns a special object "dict_keys". The behaviour of Python 2 is more a side effect than an expected behaviour.

Python 2:

>>> dict() in dict(key="value")
TypeError: unhashable type: 'dict'

Python 3 behaviour is correct.
msg207739 - (view) Author: Martin Häcker (dwt) Date: 2014-01-09 14:59
Sorry, I got the title wrong on the first try. (Already corrected).

I think the problem is that the API of dict.keys() is surprising. One gets back something that behaves like a list, the name 'keys' suggests that it is a list and for lists there is no requirement that their containing items need to be hasheable.

Now of course it makes no sense to check if a dict (not washable because it's mutable) is a key in a dictionary - but, the fact that keys() returns something else than a list is surprising and shouldn't be so. (I suspect it's a performance enhancement).

Why this shouldn't be so? I think it's because of composeability. If I expect something list like from an API I want to be able to hand it around in my application to everywhere where a list is allowed, and I certainly don't want to check beforehand if something I want to check is included in that list is hasheable for a specific subset of those lists.

Thats why I think it's a really bad idea to change the behavior of dict.keys() _not_ to return a list or something that at least behaves the same way as a list.

Should this have been done for performance reasons, it would be easy to say in that list that anything that is not hasheable cannot be in that list and therefore to return False in that case. Contract fulfilled.
msg207746 - (view) Author: STINNER Victor (vstinner) * (Python committer) Date: 2014-01-09 16:34
dict.keys() has been changed 5 years ago, when Python 3 was created.

dict.keys() is now a nice read-only view of dictionary keys. When the
dictionary is updated, the view is also updated. See the
documentation:
http://docs.python.org/3/library/stdtypes.html#dict-views

The view is not a sequence: isinstance({}.keys(),
collections.Sequence) is False.

If you want a list, you must write list(dict.keys())

> Now of course it makes no sense to check if a dict (not washable because it's mutable) is a key in a dictionary

I still don't understand your problem. What is your usecase?

Yes, Python 3 is a new language. It's a better language in my opinion,
because it now has the good behaviour.

> Should this have been done for performance reasons,

Yes, avoiding a temporary list is more efficient in "for k in dict:
..." and "for k, v in dict.items(): ...".
msg207827 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2014-01-10 02:38
> I think the problem is that the API of dict.keys() is surprising.
> One gets back something that behaves like a list, the name 'keys'
> suggests that it is a list and for lists there is no requirement 
> that their containing items need to be hasheable.

The keys() method returns a view with set-like behavior (it supports union, intersection, difference, fast membership testing using hashed lookups, and iteration).

Guido modeled this behavior from a well established API in Java.

FWIW, it is hard for us to do anything about comments like "I was surprised ..."   The language behaviors are documented but that doesn't help if the docs aren't read.  If you expected a list-like object but received a set-like object, then you would get surprised.  There is not much that can be done about that.
msg207904 - (view) Author: Martin Häcker (dwt) Date: 2014-01-11 10:03
Well, if that's the case, then this bug indeed can be closed. You switched from list as the base type to set and that has to be dealt with on application side.

Still this is surprising, but there's not much that can be done.

:-(
History
Date User Action Args
2022-04-11 14:57:56adminsetgithub: 64389
2014-01-11 10:03:29dwtsetmessages: + msg207904
2014-01-10 02:38:36rhettingersetnosy: + rhettinger
messages: + msg207827
2014-01-09 16:34:04vstinnersetmessages: + msg207746
2014-01-09 14:59:45dwtsetmessages: + msg207739
2014-01-08 13:38:45vstinnersetstatus: open -> closed

nosy: + vstinner
messages: + msg207684

resolution: not a bug
2014-01-08 13:35:18dwtsettitle: dict() in dict(foo='bar') raises -> dict() in dict(foo='bar').keys() raises
2014-01-08 13:35:00dwtcreate