[PYTHON] Use get as much as possible for dictionary-type references

Two ways to reference a dictionary (Dict) type in Python

There are two ways to look up the value of a dictionary-type object in Python.

  1. Refer as it is
  2. Use the get method

~~ It is better not to use the former, but ~~ * Corrected. See comments * It is better not to use the former much, but I will explain the reason.

1. When referring as it is

Create a simple class for illustration.

sample_class.py


class SampleDict1:

    def __init__(self, params: dict):
        self.name = params['name']
        self.age = params['age']
        self.sex = params['sex']

This is a method of passing the information to be created as a dictionary type argument when creating an instance. I think that you may do this when you store the created data in JSON in the request body of the API, convert it to a dictionary type on the server side, and pass it to the model layer.

Now let's create an instance of this class.

from sample_class import SampleDict1

params = {'name':'test',
          'age':38,
          'sex':'male'}
sample_dict1 = SampleDict1(params)
print(sample_dict1.__dict__)
# {'name': 'test', 'age': 38, 'sex': 'male'}

I was able to create it safely. If the dictionary type object passed when creating the instance does not lack parameters, there is no problem. Now let's look at the case where the dictionary object does not contain sex.

from sample_class import SampleDict1

params = {'name':'test',
          'age':38}
sample_dict1 = SampleDict1(params)
# KeyError: 'sex'

If you are directly referencing a dictionary type object and the specified key does not exist in the object, an exception called KeyError will occur. Therefore, for example, when the sex parameter is not required, the direct reference method should be implemented as follows.

sample_class.py


class SampleDict1:

    def __init__(self, params: dict):
        self.name = params['name']
        self.age = params['age']
        if 'sex' in params.keys():
            self.sex = params['sex']

Or

sample_class.py


class SampleDict1:

    def __init__(self, params: dict):
        try:
            self.name = params['name']
            self.age = params['age']
            self.sex = params['sex']
        except KeyError:
            pass

… The latter approach is completely ridiculous, but can be found in sources with a lot of code written by unskilled people. In this way, the method of directly referencing the dictionary type object may require unnecessary validation and exception handling.

2. Use the get method

So what happens when you use the get method to look up the value of a dictionary object?

sample_class.py


class SampleDict2:

    def __init__(self, params):
        self.name = params.get('name')
        self.age = params.get('age')
        self.sex = params.get('sex')

Let's create an instance.

from sample_class import SampleDict2

params = {'name':'test',
          'age':38,
          'sex':'male'}
sample_dict2 = SampleDict2(params)
print(sample_dict2.__dict__)
# {'name': 'test', 'age': 38, 'sex': 'male'}

If there are no shortages in the parameters, it was created normally as in the case of 1. So what if you don't have enough parameters?

from sample_class import SampleDict1

params = {'name':'test',
          'age':38}
sample_dict1 = SampleDict1(params)
print(sample_dict2.__dict__)
# {'name': 'test', 'age': 38, 'sex': None}

In this way, None was stored without a KeyError. In this case, no additional processing is required, such as when the sex parameter is not required. In addition, readability is not impaired by the absence of unnecessary validation and exception handling.

Furthermore, even if you want to say'unknown'when the sex parameter has no value, you can write it more clearly than when it is 1. First, if you want to refer to 1 directly, you will implement it as follows.

sample_class.py


class SampleDict1:

    def __init__(self, params: dict):
        self.name = params['name']
        self.age = params['age']
        if 'sex' in params.keys():
            self.sex = params['sex']
        else:
            self.sex = 'unknown'

On the other hand, when using the get method of 2, it can be implemented as follows.

sample_class.py


class SampleDict2:

    def __init__(self, params):
        self.name = params.get('name')
        self.age = params.get('age')
        self.sex = params.get('sex', 'unknown')
        # dict.get(key[, default])
        # Return the value for key if key is in the dictionary, else default.

In this way, when setting the default value, you can write in 1 line when using the get method of 2 while using 4 lines when using the direct reference method of 1.

in conclusion

As we have seen above, when referencing the value of a dictionary type object, it is more convenient to use the get method of 2 than the method of directly referencing 1. So, if you're writing code that is directly referenced, change all that code to get methods right away (urgently).

Supplement

Regarding the last example, even in the case of 1, you can write as follows, but we do not recommend it. This is because one line becomes longer and it becomes easier to violate the rule of less than 80 lines defined by PEP8, and to avoid it, a line break with a \ is terribly unreadable and maintainable. I think it's still better to divide it into four lines than to do this.

sample_class.py


class SampleDict1:

    def __init__(self, params: dict):
        self.name = params['name']
        self.age = params['age']
        self.sex = params['sex'] if 'sex' in params.keys() else 'unknown'
            

Recommended Posts

Use get as much as possible for dictionary-type references
Use BMFont as the font for pyglet
AtCoder: Python: Automate sample testing as much as possible.
Use Flask as the next step for SimpleHTTPServer
Use weak references