Why Square Brackets in Dictionaries

Hello,

Why do I need to use square brackets when accessing the value from a dictionary like in the code below:

d = {}
d[“laptop”] = 40000
d[“mobile”] = 15000
d[“earphone”] = 1000
print(d[“mobile”])

There is another way:

print(d.get("laptop"))

The square brackets and also the use of .get(...) are the syntax of Python that has been chosen by the language designers.

Because that’s the Python syntax for referencing a key or index in an enumerable object. You might as well ask why you need to do it for accessing specific elements in a list, tuple or string as well.

On the other hand, you don’t have to use square brackets to access dictionary values, you could instead use the get method like so:

d = {}
>>> d = {"laptop": 40000, "mobile": 15000, "earphone": 1000}
>>> print(d.get("mobile"))
15000

(Though note that get will return a default value of None if the requested key does not exist, rather than raising a KeyError exception; a different default value can be optionally specified like d.get("antenna", 0) if you want.)

Thank you Paddy3118. I’m a beginner and it’s a little confusing when I’m learning that dictionaries uses curly brackets but then learn that I have to use these other type of brackets to access the value.

2 Likes

Perhaps you are generalizing from the accident that s are used for both list displays and indexing lists. Note, for instance, (1,2,3)[1] == 2.

I see, as a learner, it may at first confuse. It may be helpful to think of their being two “areas” here, Syntax for creating the dictionary, and syntax for accessing its values.
There are several datatypes of collections of values that it would be good to have short and succinct ways of creating initial values for - e.g. lists, tuples, sets, and dicts. Their syntax use the common types of brackets: square, round, curly, and curly-with-colons.
The other idea is indexing. you use numeric indexing to access lists and tuples, but you can also index dicts. In Python, indexing is by square brackets so the indexable lists, tuples and dicts all can use square brackets when indexing.

Well, that’s how I think of it :slight_smile:

1 Like

Thank you for the explanation

Thank you for this. Is it a bad thing to get None rather than the KeyError? Also, is there a pro or con to using d.get(“antenna”, 0) and getting 0 if the key does not exist?

It’s all a question of what sort of control flow and error handling you want to implement in your program. Raising KeyError will abruptly stop the program and print a traceback to the user of where it crashed, unless you implement a try/except block to catch and handle that exception. Similarly, if you don’t expect the lookup to return None or 0 then you likely will want a conditional check on the output in order to implement whatever fallback action you would want the program to take (such as printing a clear error message to the user).

It really depends on what you’re doing.

Getting None instead of a KeyError when getting items from a dict may be exactly what you want in some cases but in others you might want to have the KeyError be raised.

So how would these two situations differ?
To me it’s all about whether you’re going to be able to deal with this default value (None in this case).
If getting a None value is expected and the subsequent code deals with it correctly, then that’s fine. You can use the .get method and be ready to deal with the consequences.
But if the subsequent code wouldn’t know what to do with a None value, you’re better off with the KeyError. That’s because this gives you a better indicator as to what went wrong and where. If you let the None value propagate it may become quite difficult to figure out where it came from since the exception raised can be far away from where the value was obtained.

Same goes for the manually provided default (0). If you want to guarantee a value for every key, then that’s a decent way to do that. But if the implementation is likely to break when using a key that’s not in the original dict, then you should just let it fail there with a KeyError instead of dragging things futher.

For example, something like this is when a default value of None would be a bad idea:

in_dict = {"a": 1, "b": 2}
keys_to_query = ['a', 'b', 'c', 'd']
out_dict = {}
for key in keys_to_query:
    val = in_dict.get(key)
    out_dict[val] = key
print(out_dict)
# prints: {1: 'a', 2: 'b', None: 'd'}

As we can see, this will first set out_dict[None] = 'c' and later out_dict[None] = 'd'.

At the same time, if you just want a default value, this might all be fine:

in_dict = {"a": 1, "b": 2}
keys_to_query = ['a', 'b', 'c', 'd']
for key in keys_to_query:
    print(key, ":", in_dict.get(key, 'N/A'))

Um, sorry but access by square brackets and by the .get method are not exactly the same, sometimes you want the exception, as it signals a true error in the algorithm or its found an issue with data. The issue with data can arrise. for example, when reading logs and for every X seen, the log later has an X_foo. If your program reads X, computes modified X_foo data then replaces the later log entry when it sees X_foo mentioned, then your program might look for <some_prefix>_foo in the log and automatically look up the data in the dict at modified_data[prefix_foo] if the prog had not first seen the prefix on its own - in this case X rthen the indexing of modified_data["X_foo"] should fail leading to a debugging session to work out what went wrong.