80

None seems to work as a dictionary key, but I am wondering if that will just lead to trouble later. For example, this works:

>>> x={'a':1, 'b':2, None:3}
>>> x
{'a': 1, None: 3, 'b': 2}
>>> x[None]
3

The actual data I am working with is educational standards. Every standard is associated with a content area. Some standards are also associated with content subareas. I would like to make a nested dictionary of the form {contentArea:{contentSubArea:[standards]}}. Some of those contentSubArea keys would be None.

In particular, I am wondering if this will lead to confusion if I look for a key that does not exist at some point, or something unanticipated like that.

Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
japhyr
  • 1,710
  • 2
  • 18
  • 24
  • 4
    What does cause trouble is using `NaN` as a key. It's deceptive, because it's hashable, but doesn't compare equal to itself. That is: `float('nan') == float('nan')` returns `False`, as it should, on two Python implementations I've tried. If you use it as a key in a dictionary, it won't raise an exception, and using it as an index will work if and only if it is the same `NaN` object (has the same `id(..)`) — though this might be implementation-dependent. So it might work as expected for a while, and then fail. None of these troubles affect `None`, even just because there is only one `None`. – Evgeni Sergeev May 24 '16 at 04:43

6 Answers6

98

Any hashable value is a valid Python Dictionary Key. For this reason, None is a perfectly valid candidate. There's no confusion when looking for non-existent keys - the presence of None as a key would not affect the ability to check for whether another key was present. Ex:

>>> d = {1: 'a', 2: 'b', None: 'c'}
>>> 1 in d
True
>>> 5 in d
False
>>> None in d
True

There's no conflict, and you can test for it just like normal. It shouldn't cause you a problem. The standard 1-to-1 Key-Value association still exists, so you can't have multiple things in the None key, but using None as a key shouldn't pose a problem by itself.

g.d.d.c
  • 46,865
  • 9
  • 101
  • 111
  • 1
    The problem is when you want to sort the dict by key. I suppose for such case you need to create special object for replacing the None type. – fmelan Jun 01 '20 at 13:49
23

You want trouble? here we go:

>>> json.loads(json.dumps({None:None}))
{u'null': None}

So yea, better stay away from json if you do use None as a key. You can patch this by custom (de/)serializer, but I would advise against use of None as a key in the first place.

Maciej Urbański
  • 475
  • 3
  • 12
9

None is not special in any particular way, it's just another python value. Its only distinction is that it happens to be the return value of a function that doesn't specify any other return value, and it also happens to be a common default value (the default arg of dict.get(), for instance).

You won't cause any run-time conflicts using such a key, but you should ask yourself if that's really a meaningful value to use for a key. It's often more helpful, from the point of view of reading code and understanding what it does, to use a designated instance for special values. Something like:

NoSubContent = SubContentArea(name=None)

{"contentArea": 
    {NoSubContent:[standards], 
     SubContentArea(name="Fruits"): ['apples', 'bananas']}}
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
  • 1
    Doesn't this approach just move the use of None down a level? I was thinking it might be better to only set values that have meanings. Then None represents empty data, and errors indicate problems with data (assuming proper validation and requirements such as allowing null or requiring not null). – japhyr Aug 11 '11 at 17:23
  • 1
    Like I said, there's nothing really special about `None`, and I don't think I'd hesitate to use it as a key in a dict if it were obvious what that must mean for that dict; but if the dict is reasonably heterogeneous, or visible across a large part of the program; adding some meaning to the type of the key could be helpful. It's not so much a question of avoiding the `None` value as picking a unity value with a type that explains itself better than `None` can. – SingleNegationElimination Aug 11 '11 at 17:31
  • I accepted gddc's answer, but upvoted this answer as well. Using None is working so far, but I can see the advantages to creating a specific key to represent None in a specific context. – japhyr Aug 11 '11 at 22:10
  • I don't think you want to 'use a key to represent `None`' that's absurd, just use `None` if you need to represent `None`. On the other hand, you might use `None` to represent a degenerate case of your particular application domain, or that could be confusing so you use a designated instance of another class to represent that degenerate case. – SingleNegationElimination Aug 12 '11 at 13:03
  • Is `None` in any possible way similar to 'void' data type present in C/C++. – Krishna Oza May 31 '15 at 06:01
4

jsonify does not support a dictionary with None key.

From Flask import jsonify

def json_():
    d = {None: 'None'}
    return jsonify(d)

This will throw an error:
TypeError: '<' not supported between instances of 'NoneType' and 'str'

adam shamsudeen
  • 1,752
  • 15
  • 14
3

It seems to me, the larger, later problem is this. If your process is creating pairs and some pairs have a "None" key, then it will overwrite all the previous None pairs. Your dictionary will silently throw out values because you had duplicate None keys. No?

  • I was going to use None as a key, thought "I'll just see what stackoverflow has to say about it" This persuaded me not to – Dan Mar 22 '15 at 11:27
  • 3
    What if you had a "5" key. Wouldn't that get silently overwritten too? Why is the None object special in that way? – Mad Physicist Dec 31 '15 at 13:24
  • @MadPhysicist - It's _not_. Dictionaries are always 1-to-1 key-to-value containers. Reassigning _any_ key will replace whatever was in the key previously. – g.d.d.c Feb 26 '16 at 15:33
  • 2
    @g.d.d.c. I agree. My point is then that `None` is just as valid a key as `5`. There's no good reason not to use it if it is convenient to do so. – Mad Physicist Feb 26 '16 at 16:01
0

Funny though, even this works :

d = {None: 'None'}

In [10]: None in d
Out[10]: True
sb32134
  • 426
  • 8
  • 19