Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"TypeError: unhashable type" could often be more clear #85286

Closed
sfreilich mannequin opened this issue Jun 25, 2020 · 11 comments
Closed

"TypeError: unhashable type" could often be more clear #85286

sfreilich mannequin opened this issue Jun 25, 2020 · 11 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@sfreilich
Copy link
Mannequin

sfreilich mannequin commented Jun 25, 2020

BPO 41114
Nosy @rhettinger, @sfreilich, @iritkatriel

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = <Date 2020-09-28.04:22:45.649>
created_at = <Date 2020-06-25.12:48:17.484>
labels = ['interpreter-core', 'type-feature']
title = '"TypeError: unhashable type" could often be more clear'
updated_at = <Date 2021-11-16.04:54:39.685>
user = 'https://github.com/sfreilich'

bugs.python.org fields:

activity = <Date 2021-11-16.04:54:39.685>
actor = 'drakebohan'
assignee = 'none'
closed = True
closed_date = <Date 2020-09-28.04:22:45.649>
closer = 'rhettinger'
components = ['Interpreter Core']
creation = <Date 2020-06-25.12:48:17.484>
creator = 'sfreilich'
dependencies = []
files = []
hgrepos = []
issue_num = 41114
keywords = []
message_count = 11.0
messages = ['372366', '377552', '377559', '377576', '377583', '377585', '377599', '377609', '377645', '378557', '406385']
nosy_count = 4.0
nosy_names = ['rhettinger', 'sfreilich', 'iritkatriel', 'drakebohan']
pr_nums = []
priority = 'normal'
resolution = 'rejected'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue41114'
versions = []

@sfreilich
Copy link
Mannequin Author

sfreilich mannequin commented Jun 25, 2020

Currently, if you (for example) put a dict as a value in a set or key in a dict, you get:

TypeError: unhashable type: 'dict'

I'm pretty sure this wording goes back a long time, but I've noticed that Python beginners tend to find this really confusing. It fits into a pattern of error messages where you have to explain why the error message is worded the way it is after you explain why the error message occurs at all. There are many instances of:
https://stackoverflow.com/questions/13264511/typeerror-unhashable-type-dict

It would be clearer if the message was something like:

TypeError: 'dict' can not be used as a set value because it is an unhashable type.

(Or "dict key" instead of "set value".)

The exception is raised in PyObject_Hash, so that doesn't have some of the context about how/why hash was called. That's called in a lot of places.

Possibly, PyObject_Hash and PyObject_HashNotImplemented could take the format string passed to PyErr_Format as an optional second argument, defaulting to the current behavior? Then certain callers (in particular, the set and dict constructor, set and dict methods that add set values or add/modify dict keys) could provide clearer error messages.

@sfreilich sfreilich mannequin added type-bug An unexpected behavior, bug, or error labels Jun 25, 2020
@iritkatriel iritkatriel added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement and removed type-bug An unexpected behavior, bug, or error labels Sep 27, 2020
@rhettinger
Copy link
Contributor

It would be clearer if the message was something like:

TypeError: 'dict' can not be used as a set value because
it is an unhashable type.

IMO that doesn't help. The trackback already shows the code where the exception occurred. The user already knows the calling type, and the existing message tells them what type was passed in.

Their real problem is that they still have to confront the notion of what "unhashable type" means and then need figure-out a way to fix it. So the proposed extra information is just a distractor that doesn't get the user any closer to understanding or solving their problem.

To get a user closer to the solution they would either need an explanation of what hashable means and/or be given a suggestion of how to fix it:

TypeError: unhashable type: 'dict'. Consider using an int,
str, tuple, or frozenset.

That said, it is precarious to guess what the user intended, so it is difficult to suggest some form of "did you mean ..."

@iritkatriel
Copy link
Member

Do you think it would help if the exception was of type UnhashableType (subclass of TypeError)? This would give the user a hint that this error is a thing which is worth looking for in the docs and understanding.

@rhettinger
Copy link
Contributor

Do you think it would help if the exception was of type UnhashableType

That would not add more information. The message already says "unhashable type". Saying it twice doesn't help.

@sfreilich
Copy link
Mannequin Author

sfreilich mannequin commented Sep 27, 2020

The user already knows

The example I link to in the initial description appears to be one case where the user does not in fact know.

I do think context that this restriction applies to dict key in particular is very relevant. The line could use the same type for both the key and the value in a dict assignment, for example.

TypeError: unhashable type: 'dict'. Consider using an int, str, tuple, or frozenset.

That seems like a pretty reasonable wording, though I think mentioning "dictionary key" or "set item" specifically still helps.

It could also link to the documentation directly:
https://docs.python.org/3/glossary.html#term-hashable

Though other error messages don't generally follow that pattern.

Saying it twice doesn't help.

As the comment you were responding to noted, putting it in the type implies there might be additional information in documentation (or at least provides a place in documentation to put that information). TypeError is too general to say something about that specifically:
https://docs.python.org/3/library/exceptions.html#TypeError

@rhettinger
Copy link
Contributor

I've noticed that Python beginners tend to find this really confusing.

No minor tweak to the exception message will make this go away. For understanding to occur, the only way to forward is to learn a bit about hashability. That is a step that every beginner must take.

Fortunately, the term "hashable" is listed in the glossary. Also the error message itself is easily Googled:

https://docs.python.org/3/glossary.html#term-hashable

https://www.google.com/search?q=TypeError%3A+unhashable+type%3A+%27dict%27&oq=TypeError%3A+unhashable+type%3A+%27dict%27

I suggest that you take this to the python-ideas list. While there is a valid concern that a new user may not understand the error message (this is unfortunately true for many our error messages), the proposals don't actually improve the situation.

The first proposal adds hard-to-implement context information that still doesn't tell a user what the issue is. The second proposal repeats information that is already being shown. Neither proposal explains what is meant by unhashable type, why it matters, what would be an allowable hashable type, or how to fix the problem (which is what the user really wants to know).

After a discussion on python-ideas, if a better proposal is found, feel free to reopen this issue.

@sfreilich
Copy link
Mannequin Author

sfreilich mannequin commented Sep 28, 2020

No minor tweak to the exception message will make this go away. For understanding to occur, the only way to forward is to learn a bit about hashability. That is a step that every beginner must take.

This is a derisive and beginner-hostile response that ignores half of what's been said by other participants in this thread.

Also the error message itself is easily Googled

Yeah, the first thing that comes up is ~4k Stack Overflow entries where people are really confused by the error message.

@iritkatriel
Copy link
Member

I think it's reasonable to discuss the problem on python-ideas rather than on a bugs issue, when it's not obvious what the right solution is.

@sfreilich
Copy link
Mannequin Author

sfreilich mannequin commented Sep 28, 2020

I think it's reasonable to discuss the problem on python-ideas rather than on a bugs issue, when it's not obvious what the right solution is.

I did start a thread there. Don't object to that, if that's a better forum for this sort of thing.

@sfreilich
Copy link
Mannequin Author

sfreilich mannequin commented Oct 13, 2020

python-ideas thread: https://mail.python.org/archives/list/python-ideas@python.org/thread/B6OMGYIM47OVGOCZLEY3MEUJDFURJRDV/

The most minimal ideas from that seem to be:

  1. Maybe link to the glossary from the error message (if links to documentation in error messages are permissible).

  2. Add a glossary entry for "unhashable" for the sake of completeness (similar to how there are entries for both "immutable" and "mutable").

@drakebohan
Copy link
Mannequin

drakebohan mannequin commented Nov 16, 2021

The problem is that you can't use a list as the key in a dict, since dict keys need to be immutable. This means that when you try to hash an unhashable object it will result an error. For ex. when you use a list as a key in the dictionary , this cannot be done because lists can't be hashed. The standard way to solve this issue is to cast a list to a tuple . TypeError: unhashable type: 'list' usually means that you are trying to use a list as an hash argument. The standard way to solve this issue is to cast a list to tuple.

Though tuples may seem similar to lists, they are often used in different situations and for different purposes. Tuples can be used as keys if they contain only strings, numbers, or tuples; if a tuple contains any mutable object either directly or indirectly, it cannot be used as a key. You can’t use lists as key.

http://net-informations.com/python/iq/unhashable.htm

@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants