I was trying out some various one-line solutions to the problem of defining a variable only if it does not already exist and noticed that Python handles dicts and lists/tuples differently. These errors seem entirely parallel to me, so I'm confused why there is discrepancy.
Dictionary KeyError Handling
existing_dict = {"spam": 1, "eggs": 2}
existing_dict["foo"] = existing_dict["foo"] if not KeyError else 3
Returns {"spam": 1, "eggs": 2, "foo": 3}
Notice that I'm referencing a non-existing key in both left and right hand sides; Python has no problem handling the KeyError in either clause where it appears.
List IndexError Handling (also true for tuples)
existing_list = ["spam","eggs"]
existing_list[2] = existing_list[2] if not IndexError else ["foo"]
Returns IndexError: list assignment index out of range
It's not difficult at all to get around this specific error (answer here), but I'm curious why there is a difference in these cases. In both situations, there seems to be an error in both assignee/assignment clauses with one "if not" error catch.
In both cases, KeyError
and IndexError
are just classes and are both true:
>>> bool(KeyError)
True
>>> bool(IndexError)
True
All class objects test as true in Python, see Truth Value Testing.
You cannot use conditional expressions to test for an exception; for both your examples, the else
value is picked always, then assigned; your tests are exactly equivalent to:
existing_dict["foo"] = 3
existing_list[2] = ["foo"]
You'd use exception handling instead, or use a length test.
The exception is caused because assignment to list indexes only works if the index already exists:
>>> empty = []
>>> empty[0] = None
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
This is simply a difference in how a dictionary and a list works; lists you can append to, which will grow the number of indices. You cannot do this with a dictionary (there is no order), so to add a new key-value pair you need to assign to it. On the other hand, if lists supported arbitrary index assignment, what would happen with all the indices in between? What if the list was empty but you assigned to index 42; what happens to the indices 0-41?
Either catch the exception with try
/except
:
try:
existing_list[2] = "foo"
except IndexError:
existing.append('foo')
This replaces the existing value at index 2, or appends if the index doesn't yet exist.
You could try to test for the length:
if len(existing_list) <= 3:
existing_list.append('foo')
and it'll be appended only if there are not yet at least 3 elements.
For dictionaries, test for the key:
if 'foo' not in existing_dict:
existing_dict['foo'] = 3