dictionaries

preview

Here are the tests I have by the end of the chapter

  1import unittest
  2
  3
  4class TestDictionaries(unittest.TestCase):
  5
  6    def test_making_a_dictionary(self):
  7        self.assertEqual(dict(), {})
  8        self.assertEqual(dict(key='value'), {'key': 'value'})
  9
 10    def test_making_a_dictionary_w_none_as_a_key(self):
 11        self.assertEqual({None: 'boom'}, {None: 'boom'})
 12
 13    def test_making_a_dictionary_w_a_boolean_as_a_key(self):
 14        self.assertEqual(
 15            {False: 'boom', True: 'bap'},
 16            {False: 'boom', True: 'bap'}
 17        )
 18
 19    def test_making_a_dictionary_w_a_number_as_a_key(self):
 20        self.assertEqual(
 21            {0: 'boom', 0.1: 'bap'},
 22            {0: 'boom', 0.1: 'bap'}
 23        )
 24
 25    def test_making_a_dictionary_w_a_tuple_as_a_key(self):
 26        self.assertEqual(
 27            {(0, 1): 'boom'},
 28            {(0, 1): 'boom'}
 29        )
 30
 31    def test_making_a_dictionary_w_a_list_as_a_key(self):
 32        with self.assertRaises(TypeError):
 33            {[3, 2, 1]: 'BOOM!!!'}
 34
 35    def test_making_a_dictionary_w_a_set_as_a_key(self):
 36        with self.assertRaises(TypeError):
 37            {{3, 2, 1}: 'BOOM!!!'}
 38
 39    def test_making_a_dictionary_w_a_dictionary_as_a_key(self):
 40        a_dictionary = {'key': 'value'}
 41        with self.assertRaises(TypeError):
 42            {a_dictionary: 'BOOM!!!'}
 43
 44    def test_attributes_and_methods_of_dictionaries(self):
 45        self.maxDiff = None
 46        self.assertEqual(
 47            dir(dict),
 48            [
 49                '__class__',
 50                '__class_getitem__',
 51                '__contains__',
 52                '__delattr__',
 53                '__delitem__',
 54                '__dir__',
 55                '__doc__',
 56                '__eq__',
 57                '__format__',
 58                '__ge__',
 59                '__getattribute__',
 60                '__getitem__',
 61                '__getstate__',
 62                '__gt__',
 63                '__hash__',
 64                '__init__',
 65                '__init_subclass__',
 66                '__ior__',
 67                '__iter__',
 68                '__le__',
 69                '__len__',
 70                '__lt__',
 71                '__ne__',
 72                '__new__',
 73                '__or__',
 74                '__reduce__',
 75                '__reduce_ex__',
 76                '__repr__',
 77                '__reversed__',
 78                '__ror__',
 79                '__setattr__',
 80                '__setitem__',
 81                '__sizeof__',
 82                '__str__',
 83                '__subclasshook__',
 84                'clear',
 85                'copy',
 86                'fromkeys',
 87                'get',
 88                'items',
 89                'keys',
 90                'pop',
 91                'popitem',
 92                'setdefault',
 93                'update',
 94                'values'
 95            ]
 96        )
 97
 98    def test_clear_empties_a_dictionary(self):
 99        a_dictionary = {'key': 'value'}
100        self.assertIsNone(a_dictionary.clear())
101        self.assertEqual(a_dictionary, {})
102
103    def test_copy_a_dictionary(self):
104        a_dictionary = {'key': 'value'}
105        self.assertEqual(
106            a_dictionary.copy(),
107            {'key': 'value'}
108        )
109
110    def test_fromkeys_makes_a_dictionary_from_an_iterable(self):
111        self.assertEqual(
112            dict.fromkeys((0, 1), 'default'),
113            {0: 'default', 1: 'default'}
114        )
115
116    def test_get_value_of_a_key_in_a_dictionary(self):
117        a_dictionary = {'key': 'value'}
118        self.assertEqual(
119            a_dictionary.get('not_in_dictionary', 'default'),
120            'default'
121        )
122        self.assertEqual(
123            a_dictionary.get('key', 'default'),
124            'value'
125        )
126
127    def test_items_returns_iterable_of_key_value_pairs_of_a_dictionary(self):
128        a_dictionary = {
129            'key1': 'value1',
130            'keyN': [0, 1, 2, 'n'],
131        }
132        self.assertEqual(
133            list(a_dictionary.items()),
134            [
135                ('key1', 'value1'),
136                ('keyN', [0, 1, 2, 'n']),
137            ]
138        )
139
140    def test_keys_of_a_dictionary(self):
141        a_dictionary = {
142            'key1': 'value1',
143            'keyN': [0, 1, 2, 'n'],
144        }
145        self.assertEqual(
146            list(a_dictionary.keys()),
147            ['key1', 'keyN']
148        )
149
150    def test_pop_removes_given_key_from_a_dictionary_and_returns_its_value(self):
151        a_dictionary = {'key': 'value'}
152
153        with self.assertRaises(KeyError):
154            a_dictionary.pop('not_in_dictionary')
155        self.assertEqual(
156            a_dictionary.pop('not_in_dictionary', 'default'),
157            'default'
158        )
159        self.assertEqual(
160            a_dictionary.pop('key', 'default'),
161            'value'
162        )
163        self.assertEqual(a_dictionary, {})
164
165    def test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary(self):
166        a_dictionary = {
167            'key1': 'value1',
168            'keyN': [0, 1, 2, 'n'],
169        }
170        self.assertEqual(
171            a_dictionary.popitem(),
172            ('keyN', [0, 1, 2, 'n'])
173        )
174        self.assertEqual(a_dictionary, {'key1': 'value1'})
175
176    def test_setdefault_adds_given_key_to_a_dictionary(self):
177        a_dictionary = {'key': 'value'}
178        self.assertEqual(
179            a_dictionary.setdefault('new_key', 'default'),
180            'default'
181        )
182        self.assertEqual(
183            a_dictionary.setdefault('key', 'default'),
184            'value'
185        )
186        self.assertEqual(
187            a_dictionary,
188            {
189                'key': 'value',
190                'new_key': 'default',
191            }
192        )
193
194    def test_update_a_dictionary(self):
195        a_dictionary = {'key': 'value'}
196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
197        self.assertIsNone(a_dictionary.update(key='updated value'))
198        self.assertIsNone(a_dictionary.update({
199            'another_key': {0, 1, 2, 'n'}
200        }))
201        self.assertEqual(
202            a_dictionary,
203            {
204                'key': 'updated value',
205                'new_key': [0, 1, 2, 'n'],
206                'another_key': {0, 1, 2, 'n'},
207            }
208        )
209
210    def test_values_of_a_dictionary(self):
211        a_dictionary = {
212            'key1': 'value1',
213            'keyN': [0, 1, 2, 'n'],
214        }
215        self.assertEqual(
216            list(a_dictionary.values()),
217            [
218                'value1',
219                [0, 1, 2, 'n'],
220            ]
221        )
222
223    def test_key_error(self):
224        a_dictionary = {'key': 'value'}
225        self.assertEqual(a_dictionary['key'], 'value')
226
227        with self.assertRaises(KeyError):
228            a_dictionary['not_in_dictionary']
229        self.assertEqual(
230            a_dictionary.get('not_in_dictionary', 'default'),
231            'default'
232        )
233
234        with self.assertRaises(KeyError):
235            a_dictionary.pop('not_in_dictionary')
236        self.assertEqual(
237            a_dictionary.pop('not_in_dictionary', 'default'),
238            'default'
239        )
240
241        with self.assertRaises(KeyError):
242            {}.popitem()
243
244
245# Exceptions seen
246# AssertionError
247# TypeError
248# NameError
249# KeyError

questions about dictionaries

Here are questions you can answer after going through this chapter


start the project

  • I name this project dictionaries

  • I open makePythonTdd.sh or makePythonTdd.ps1 in the editor

    Tip

    Here is a quick way to open makePythonTdd.sh or makePythonTdd.ps1 if you are using Visual Studio Code

    code makePythonTdd.sh
    

    on Windows without Windows Subsystem for Linux use

    code makePythonTdd.ps1
    
  • I change everywhere I have list_comprehensions to the name of this project

     1#!/bin/bash
     2mkdir dictionaries
     3cd dictionaries
     4mkdir src
     5touch src/dictionaries.py
     6mkdir tests
     7touch tests/__init__.py
     8
     9echo "import unittest
    10
    11
    12class TestDictionaries(unittest.TestCase):
    13
    14    def test_failure(self):
    15        self.assertFalse(True)
    16
    17
    18# Exceptions seen
    19# AssertionError
    20" > tests/test_dictionaries.py
    

    Attention

    on Windows without Windows Subsystem for Linux use makePythonTdd.ps1 NOT makePythonTdd.sh

     1mkdir dictionaries
     2cd dictionaries
     3mkdir src
     4New-Item src/dictionaries.py
     5mkdir tests
     6New-Item tests/__init__.py
     7
     8"import unittest
     9
    10
    11class TestDictionaries(unittest.TestCase):
    12
    13    def test_failure(self):
    14        self.assertFalse(True)
    15
    16# Exceptions seen
    17# AssertionError
    18" | Out-File tests/test_dictionaries.py
    
  • I run the program in the terminal

    ./makePythonTdd.sh
    

    Attention

    on Windows without Windows Subsystem for Linux use makePythonTdd.ps1 NOT makePythonTdd.sh

    ./makePythonTdd.ps1
    

    the terminal shows AssertionError

    ======================================= FAILURES =======================================
    ____________________________ TestDictionaries.test_failure _____________________________
    
    self = <tests.test_dictionaries.TestDictionaries testMethod=test_failure>
    
        def test_failure(self):
    >       self.assertFalse(True)
    E       AssertionError: True is not false
    
    tests/test_dictionaries.py:7: AssertionError
    =============================== short test summary info ================================
    FAILED tests/test_dictionaries.py::TestDictionaries::test_failure - AssertionError: True is not false
    ================================== 1 failed in X.YZs ===================================
    
  • I hold ctrl (Windows/Linux) or option or command (MacOS) on the keyboard and use the mouse to click on tests/test_dictionaries.py:7 to open it in the editor

  • then I change True to False

    7        self.assertFalse(False)
    

    the test passes


test_making_a_dictionary

RED: make it fail

I change test_failure to test_making_a_dictionary then add an assertion

1import unittest
2
3
4class TestDictionaries(unittest.TestCase):
5
6    def test_making_a_dictionary(self):
7        self.assertEqual(dict(), None)

the terminal shows AssertionError

AssertionError: {} != None

GREEN: make it pass

I change the expectation to match

6    def test_making_a_dictionary(self):
7        self.assertEqual(dict(), {})

the test passes. These are two ways to make an empty dictionary

  • dict()

  • curly braces - {}

REFACTOR: make it better

  • I add another assertion, this time with input to make a dictionary with things in it

    6    def test_making_a_dictionary(self):
    7        self.assertEqual(dict(), {})
    8        self.assertEqual(dict(0), {})
    

    the terminal shows TypeError

    TypeError: 'int' object is not iterable
    
  • I add TypeError to the list of Exceptions seen

    11# Exceptions seen
    12# AssertionError
    13# TypeError
    
  • I change the value given to dict() to a tuple

    7        self.assertEqual(dict(), {})
    8        self.assertEqual(dict((0, 1)), {})
    

    the terminal shows TypeError

    TypeError: cannot convert dictionary update sequence element #0 to a sequence
    
  • I try a keyword argument

    7        self.assertEqual(dict(), {})
    8        self.assertEqual(dict(key='value'), {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    
  • I change the expectation to match the values in the terminal

    8        self.assertEqual(dict(key='value'), {'key': 'value'})
    

    the test passes.

I know how to make a dictionary. I used a string as a key in this test. Next I test other Python basic data types to see which ones I can use as keys


test_making_a_dictionary_w_none_as_a_key

RED: make it fail

I add a test to see if I can use None as a key in a dictionary

 8        self.assertEqual(dict(key='value'), {'key': 'value'})
 9
10    def test_making_a_dictionary_w_none_as_a_key(self):
11        self.assertEqual({None: 'boom'}, {None: 'bap'})
12
13
14# Exceptions seen

the terminal shows AssertionError

AssertionError: {None: 'boom'} != {None: 'bap'}

GREEN: make it pass

I change 'bap' to 'boom'

11        self.assertEqual({None: 'boom'}, {None: 'boom'})

the test passes. I can use strings and None as keys in a dictionary


test_making_a_dictionary_w_a_boolean_as_a_key

RED: make it fail

I add a test to see if I can use a boolean as a key in a dictionary

10    def test_making_a_dictionary_w_none_as_a_key(self):
11        self.assertEqual({None: 'boom'}, {None: 'boom'})
12
13    def test_making_a_dictionary_w_a_boolean_as_a_key(self):
14        self.assertEqual({False: 'boom'}, {False: 'bap'})
15
16
17# Exceptions seen

the terminal shows AssertionError

AssertionError: {False: 'boom'} != {False: 'bap'}

GREEN: make it pass

I change 'bap' to 'boom'

14        self.assertEqual({False: 'boom'}, {False: 'boom'})

the tests passes. I can use False as a key in a dictionary

REFACTOR: make it better

I add an assertion for the other boolean

13    def test_making_a_dictionary_w_a_boolean_as_a_key(self):
14        self.assertEqual(
15            {False: 'boom', True: 'bap'},
16            {False: 'boom'}
17        )

the terminal shows AssertionError

AssertionError: {False: 'boom', True: 'bap'} != {False: 'boom'}

I add the new key-value pair to the expectation

14        self.assertEqual(
15            {False: 'boom', True: 'bap'},
16            {False: 'boom', True: 'bap'}
17        )

the test passes. I can use strings, booleans and None as keys in a dictionary


test_making_a_dictionary_w_a_number_as_a_key

RED: make it fail

I add a failing test to see if I can use a number as a key in a dictionary

16            {False: 'boom', True: 'bap'}
17        )
18
19    def test_making_a_dictionary_w_a_number_as_a_key(self):
20        self.assertEqual(
21            {0: 'boom'},
22            {0: 'bap'}
23        )
24
25
26# Exceptions seen

the terminal shows AssertionError

AssertionError: {0: 'boom'} != {0: 'bap'}

GREEN: make it pass

I change 'bap' to 'boom'

20        self.assertEqual(
21            {0: 'boom'},
22            {0: 'boom'}
23        )

the test passes. I can use an integer as a key in a dictionary

REFACTOR: make it better

I want to see if I can use a float as a key in a dictionary. I add a float to the dictionary

19    def test_making_a_dictionary_w_a_number_as_a_key(self):
20        self.assertEqual(
21            {0: 'boom', 0.1: 'bap'},
22            {0: 'boom'}
23        )

the terminal shows AssertionError

AssertionError: {0: 'boom', 0.1: 'bap'} != {0: 'boom'}

I add the new key-value pair to the expectation

20        self.assertEqual(
21            {0: 'boom', 0.1: 'bap'},
22            {0: 'boom', 0.1: 'bap'}
23        )

the test passes. I can use strings, numbers (floats and integers), booleans and None as keys in a dictionary


test_making_a_dictionary_w_a_tuple_as_a_key

RED: make it fail

I add a test to see if I can use a tuple (anything in parentheses (()), separated by a comma) as a key in a dictionary

20        self.assertEqual(
21            {0: 'boom', 0.1: 'bap'},
22            {0: 'boom', 0.1: 'bap'}
23        )
24
25    def test_making_a_dictionary_w_a_tuple_as_a_key(self):
26        self.assertEqual(
27            {(0, 1): 'boom'},
28            {(0, 1): 'bap'}
29        )
30
31
32# Exceptions seen

the terminal shows AssertionError

AssertionError: {(0, 1): 'boom'} != {(0, 1): 'bap'}

GREEN: make it pass

I change 'bap' to 'boom'

26        self.assertEqual(
27            {(0, 1): 'boom'},
28            {(0, 1): 'boom'}
29        )

the test passes. I can use tuples, strings, numbers (floats and integers), booleans and None as keys in a dictionary


test_making_a_dictionary_w_a_list_as_a_key

RED: make it fail

I add a test for lists (anything in square brackets ([]))

26        self.assertEqual(
27            {(0, 1): 'boom'},
28            {(0, 1): 'boom'}
29        )
30
31    def test_making_a_dictionary_w_a_list_as_a_key(self):
32        self.assertEqual(
33            {[0, 1]: 'boom'},
34        )
35
36
37# Exceptions seen

the terminal shows TypeError

TypeError: unhashable type: 'list'

GREEN: make it pass

  • I remove the things around the new dictionary then change the key and value for fun

    31    def test_making_a_dictionary_w_a_list_as_a_key(self):
    32
    33        {[3, 2, 1]: 'BOOM!!!'}
    

    the terminal still shows TypeError

  • I add assertRaises

    31    def test_making_a_dictionary_w_a_list_as_a_key(self):
    32        with self.assertRaises(TypeError):
    33            {[3, 2, 1]: 'BOOM!!!'}
    

    the test passes. I cannot use a list as a key in a dictionary


test_making_a_dictionary_w_a_set_as_a_key

RED: make it fail

I add another test with a set (single items in curly braces ({})) as a key in a dictionary

32        with self.assertRaises(TypeError):
33            {[3, 2, 1]: 'BOOM!!!'}
34
35    def test_making_a_dictionary_w_a_set_as_a_key(self):
36        {{3, 2, 1}: 'BOOM!!!'}
37
38
39# Exceptions seen

the terminal shows TypeError

TypeError: unhashable type: 'set'

GREEN: make it pass

I add assertRaises to handle the Exception

35    def test_making_a_dictionary_w_a_set_as_a_key(self):
36        with self.assertRaises(TypeError):
37            {{3, 2, 1}: 'BOOM!!!'}

the test is green again. I cannot use lists or sets as keys in a dictionary


test_making_a_dictionary_w_a_dictionary_as_a_key

RED: make it fail

I add another test, this time for a dictionary

36        with self.assertRaises(TypeError):
37            {[3, 2, 1]: 'BOOM!!!'}
38
39    def test_making_a_dictionary_w_a_dictionary_as_a_key(self):
40        a_dictionary = {'key': 'value'}
41        {a_dictionary: 'BOOM!!!'}
42
43
44# Exceptions seen

the terminal shows TypeError

TypeError: unhashable type: 'dict'

GREEN: make it pass

I add assertRaises

39    def test_making_a_dictionary_w_a_dictionary_as_a_key(self):
40        a_dictionary = {'key': 'value'}
41        with self.assertRaises(TypeError):
42            {a_dictionary: 'BOOM!!!'}
43
44
45# Exceptions seen

the test passes. I cannot use dictionaries, sets or lists as keys in a dictionary. They are not hashable, which means they can change in their lifetime


test_attributes_and_methods_of_dictionaries

RED: make it fail

I add a new test with the dir built-in function to see the attributes and methods of dictionaries

41        with self.assertRaises(TypeError):
42            {a_dictionary: 'BOOM!!!'}
43
44    def test_attributes_and_methods_of_dictionaries(self):
45        self.assertEqual(
46            dir(dict),
47            []
48        )
49
50
51# Exceptions seen

the terminal shows AssertionError

AssertionError: Lists differ: ['__class__', '__class_getitem__', '__cont[530 chars]ues'] != []

It also gives me a message about how to show the full difference between the two lists

Diff is 720 characters long. Set self.maxDiff to None to see it.

unittest.TestCase.maxDiff is a class attribute that is used to set the maximum length of differences between 2 items that the terminal shows

GREEN: make it pass

  • I add maxDiff to the test

    44    def test_attributes_and_methods_of_dictionaries(self):
    45        self.maxDiff = None
    46        self.assertEqual(
    47            dir(dict),
    48            []
    49        )
    

    the terminal shows the full difference between the two lists.

  • I copy (ctrl/command+c) and paste (ctrl/command+v) the expected values from the terminal then use find and replace to remove the extra characters

    Note

    results can be different because of the Python version.

    44    def test_attributes_and_methods_of_dictionaries(self):
    45        self.maxDiff = None
    46        self.assertEqual(
    47            dir(dict),
    48            [
    49                '__class__',
    50                '__class_getitem__',
    51                '__contains__',
    52                '__delattr__',
    53                '__delitem__',
    54                '__dir__',
    55                '__doc__',
    56                '__eq__',
    57                '__format__',
    58                '__ge__',
    59                '__getattribute__',
    60                '__getitem__',
    61                '__getstate__',
    62                '__gt__',
    63                '__hash__',
    64                '__init__',
    65                '__init_subclass__',
    66                '__ior__',
    67                '__iter__',
    68                '__le__',
    69                '__len__',
    70                '__lt__',
    71                '__ne__',
    72                '__new__',
    73                '__or__',
    74                '__reduce__',
    75                '__reduce_ex__',
    76                '__repr__',
    77                '__reversed__',
    78                '__ror__',
    79                '__setattr__',
    80                '__setitem__',
    81                '__sizeof__',
    82                '__str__',
    83                '__subclasshook__',
    84                'clear',
    85                'copy',
    86                'fromkeys',
    87                'get',
    88                'items',
    89                'keys',
    90                'pop',
    91                'popitem',
    92                'setdefault',
    93                'update',
    94                'values'
    95            ]
    96        )
    

    the test passes

  • I copy the names that do NOT have double underscores (__) and paste them below the test to make a TODO list that I use to test what I can do with dictionaries

     93                'update',
     94                'values'
     95            ]
     96        )
     97
     98
     99'clear',
    100'copy',
    101'fromkeys',
    102'get',
    103'items',
    104'keys',
    105'pop',
    106'popitem',
    107'setdefault',
    108'update',
    109'values'
    110
    111
    112# Exceptions seen
    

    the terminal still shows green


test_clear_empties_a_dictionary

RED: make it fail

  • I add a test for the first method

     93                'update',
     94                'values'
     95            ]
     96        )
     97
     98    def test_clear(self):
     99        a_dictionary = {'key': 'value'}
    100        self.assertIsNone(a_dictionary.clear())
    101
    102
    103'clear',
    

    still green. The clear method returns None

  • I add an assertion to see what clear did to the dictionary

     98    def test_clear(self):
     99        a_dictionary = {'key': 'value'}
    100        self.assertIsNone(a_dictionary.clear())
    101        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {} != {'key': 'value'}
    

    the clear method emptied the dictionary, same as it does with lists

GREEN: make it pass

I change the values to match

 98    def test_clear(self):
 99        a_dictionary = {'key': 'value'}
100        self.assertIsNone(a_dictionary.clear())
101        self.assertEqual(a_dictionary, {})

the test passes

REFACTOR: make it better

  • I change the name of the test

     98    def test_clear_empties_a_dictionary(self):
     99        a_dictionary = {'key': 'value'}
    100        self.assertIsNone(a_dictionary.clear())
    101        self.assertEqual(a_dictionary, {})
    102
    103
    104'clear',
    
  • I remove clear from the TODO list

    101        self.assertEqual(a_dictionary, {})
    102
    103
    104'copy',
    105'fromkeys',
    106'get',
    107'items',
    108'keys',
    109'pop',
    110'popitem',
    111'setdefault',
    112'update',
    113'values'
    

I know how to empty a dictionary


test_copy_a_dictionary

RED: make it fail

I add a test for the copy method

101        self.assertEqual(a_dictionary, {})
102
103    def test_copy(self):
104        a_dictionary = {'key': 'value'}
105        self.assertIsNone(a_dictionary.copy())
106
107
108'copy',
109'fromkeys',

the terminal shows AssertionError

AssertionError: {'key': 'value'} is not None

this method returns a copy of the dictionary, same as with lists

GREEN: make it pass

I add the value to the assertion

103    def test_copy(self):
104        a_dictionary = {'key': 'value'}
105        self.assertIsNone(
106            a_dictionary.copy(),
107            {'key': 'value'}
108        )

the terminal shows AssertionError

AssertionError: {'key': 'value'} is not None : {'key': 'value'}

I change assertIsNone to assertEqual

103    def test_copy(self):
104        a_dictionary = {'key': 'value'}
105        self.assertEqual(
106            a_dictionary.copy(),
107            {'key': 'value'}
108        )

the test passes

REFACTOR: make it better

  • I change the name of the test

    103    def test_copy_a_dictionary(self):
    104        a_dictionary = {'key': 'value'}
    105        self.assertEqual(
    106            a_dictionary.copy(),
    107            {'key': 'value'}
    108        )
    109
    110
    111'copy',
    112'fromkeys',
    
  • I remove copy from the TODO list

    105        self.assertEqual(
    106            a_dictionary.copy(),
    107            {'key': 'value'}
    108        )
    109
    110
    111'fromkeys',
    112'get',
    113'items',
    114'keys',
    115'pop',
    116'popitem',
    117'setdefault',
    118'update',
    119'values'
    

I know how to copy a dictionary


test_fromkeys_makes_a_dictionary_from_an_iterable

RED: make it fail

I add a test for the next method from the TODO list

105        self.assertEqual(
106            a_dictionary.copy(),
107            {'key': 'value'}
108        )
109
110    def test_fromkeys(self):
111        a_dictionary = {'key': 'value'}
112        self.assertIsNone(a_dictionary.fromkeys())
113
114
115'fromkeys',

the terminal shows TypeError

TypeError: fromkeys expected at least 1 argument, got 0

GREEN: make it pass

  • I pass a value to the call

    110    def test_fromkeys(self):
    111        a_dictionary = {'key': 'value'}
    112        self.assertIsNone(a_dictionary.fromkeys(0))
    

    the terminal shows TypeError

    TypeError: 'int' object is not iterable
    
  • I change the value to a tuple

    112        self.assertIsNone(a_dictionary.fromkeys((0, 1)))
    

    the terminal shows AssertionError

    AssertionError: {0: None, 1: None} is not None
    

    the fromkeys method returns a dictionary that uses the values in the iterable as keys with default values of None. It reminds me of a list comprehension

  • I add the dictionary as an expectation

    110    def test_fromkeys(self):
    111        a_dictionary = {'key': 'value'}
    112        self.assertIsNone(
    113            a_dictionary.fromkeys((0, 1)),
    114            {0: None, 1: None}
    115        )
    

    the terminal shows AssertionError

    AssertionError: {0: None, 1: None} is not None : {0: None, 1: None}
    
  • I change assertIsNone to assertEqual

    112        self.assertEqual(
    113            a_dictionary.fromkeys((0, 1)),
    114            {0: None, 1: None}
    115        )
    

    the test passes

REFACTOR: make it better

  • I add another assert method to see what happened to a_dictionary

    110    def test_fromkeys(self):
    111        a_dictionary = {'key': 'value'}
    112        self.assertEqual(
    113            a_dictionary.fromkeys((0, 1)),
    114            {0: None, 1: None}
    115        )
    116        self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    the dictionary did not change

  • I remove the last line I added

  • then change the call to fromkeys to use the dict class

    110    def test_fromkeys(self):
    111        a_dictionary = {'key': 'value'}
    112        self.assertEqual(
    113            dict.fromkeys((0, 1)),
    114            {0: None, 1: None}
    115        )
    116
    117
    118'fromkeys',
    

    the test is still green

  • I remove a_dictionary since it is not used

    110    def test_fromkeys(self):
    111        self.assertEqual(
    112            dict.fromkeys((0, 1)),
    113            {0: None, 1: None}
    114        )
    115
    116
    117'fromkeys',
    

    still green

  • the dictionary made with the fromkeys method has None as the default values. When I called the method without inputs the terminal showed TypeError

    TypeError: fromkeys expected at least 1 argument, got 0
    

    I add a second input to the call to see what happens

    111        self.assertEqual(
    112            dict.fromkeys((0, 1), None),
    113            {0: None, 1: None}
    114        )
    

    the terminal still shows green

  • I change the second input expecting a failure

    111        self.assertEqual(
    112            dict.fromkeys((0, 1), 'default'),
    113            {0: None, 1: None}
    114        )
    

    the terminal shows AssertionError

    AssertionError: {0: 'default', 1: 'default'} != {0: None, 1: None}
    
  • I change the values in the expectation to match

    111        self.assertEqual(
    112            dict.fromkeys((0, 1), 'default'),
    113            {0: 'default', 1: 'default'}
    114        )
    

    the test is green again. This is like a dict comprehension because it made a dictionary using the items from the iterable as keys

  • I change the name of the test

    110    def test_fromkeys_makes_a_dictionary_from_an_iterable(self):
    111        self.assertEqual(
    112            dict.fromkeys((0, 1), 'default'),
    113            {0: 'default', 1: 'default'}
    114        )
    115
    116
    117'fromkeys',
    118'get',
    
  • I remove fromkeys from the TODO list

    111        self.assertEqual(
    112            dict.fromkeys((0, 1), 'default'),
    113            {0: 'default', 1: 'default'}
    114        )
    115
    116
    117'get',
    118'items',
    119'keys',
    120'pop',
    121'popitem',
    122'setdefault',
    123'update',
    124'values'
    

I know how to make a dictionary from an iterable


test_get_value_of_a_key_in_a_dictionary

RED: make it fail

I add a test for the get method

111        self.assertEqual(
112            dict.fromkeys((0, 1), 'default'),
113            {0: 'default', 1: 'default'}
114        )
115
116    def test_get(self):
117        a_dictionary = {'key': 'value'}
118        self.assertIsNone(a_dictionary.get())
119
120
121'get',

the terminal shows TypeError

TypeError: get expected at least 1 argument, got 0

GREEN: make it pass

I add a value to the call

118        self.assertIsNone(a_dictionary.get(0))

the test passes

REFACTOR: make it better

  • the get method also expected at least 1 argument, I add None to the call

    118        self.assertIsNone(a_dictionary.get(0, None))
    

    the terminal still shows green

  • I change the second argument expecting a failure

    118        self.assertIsNone(a_dictionary.get(0, 'default'))
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    
  • I add the expectation

    116    def test_get(self):
    117        a_dictionary = {'key': 'value'}
    118        self.assertIsNone(
    119            a_dictionary.get(0, 'default'),
    120            'default'
    121        )
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    
  • I change assertIsNone to assertEqual

    118        self.assertEqual(
    119            a_dictionary.get(0, 'default'),
    120            'default'
    121        )
    

    the test passes

  • I change 0 in the call to be clearer

    118        self.assertEqual(
    119            a_dictionary.get('not_in_dictionary', 'default'),
    120            'default'
    121        )
    

    the test is still green

  • I want to see what happens when I use the get method with a key that is in the dictionary, I add another assertion

    118        self.assertEqual(
    119            a_dictionary.get('not_in_dictionary', 'default'),
    120            'default'
    121        )
    122        self.assertEqual(
    123            a_dictionary.get('key', 'default'),
    124            'default'
    125        )
    126
    127
    128'get',
    

    the terminal shows AssertionError

    AssertionError: 'value' != 'default'
    

    I get 'value' back

  • I change the expectation to match

    122        self.assertEqual(
    123            a_dictionary.get('key', 'default'),
    124            'value'
    125        )
    

    the test passes

    Note

    The get method has a condition

  • I change the name of the test

    116    def test_get_value_of_a_key_in_a_dictionary(self):
    117        a_dictionary = {'key': 'value'}
    118        self.assertEqual(
    119            a_dictionary.get('not_in_dictionary', 'default'),
    120            'default'
    121        )
    122        self.assertEqual(
    123            a_dictionary.get('key', 'default'),
    124            'value'
    125        )
    126
    127
    128'get',
    

    the test is still green

  • I remove get from the TODO list

    122        self.assertEqual(
    123            a_dictionary.get('key', 'default'),
    124            'value'
    125        )
    126
    127
    128'items',
    129'keys',
    130'pop',
    131'popitem',
    132'setdefault',
    133'update',
    134'values'
    

I know how to get the value of a key that is in a dictionary


test_items_returns_iterable_of_key_value_pairs_of_a_dictionary

RED: make it fail

I add the next test from the TODO list

122        self.assertEqual(
123            a_dictionary.get('key', 'default'),
124            'value'
125        )
126
127    def test_items(self):
128        a_dictionary = {'key': 'value'}
129        self.assertIsNone(a_dictionary.items())
130
131
132'items',

the terminal shows AssertionError

AssertionError: dict_items([('key', 'value')]) is not None

GREEN: make it pass

  • I copy the dict_items object from the terminal and paste it as the expectation

    128        a_dictionary = {'key': 'value'}
    129        self.assertIsNone(
    130            a_dictionary.items(),
    131            dict_items([('key', 'value')])
    132        )
    

    the terminal shows NameError

    NameError: name 'dict_items' is not defined
    

    this new object has a list and I know how to work with lists, just like dict_items

  • I add NameError to the list of Exceptions seen

    145# Exceptions seen
    146# AssertionError
    147# TypeError
    148# NameError
    
  • I remove the stuff around the list

    129        self.assertIsNone(
    130            a_dictionary.items(),
    131            [('key', 'value')]
    132        )
    

    the terminal shows AssertionError

    AssertionError: dict_items([('key', 'value')]) is not None : [('key', 'value')]
    
  • I pass the call to the items method to list() to see if it is iterable

    129        self.assertIsNone(
    130            list(a_dictionary.items()),
    131            [('key', 'value')]
    132        )
    

    the terminal shows AssertionError

    AssertionError: [('key', 'value')] is not None : [('key', 'value')]
    

    the values are the same

  • I change assertIsNone to assertEqual

    129        self.assertEqual(
    130            list(a_dictionary.items()),
    131            [('key', 'value')]
    132        )
    

    the test passes.

This works because the items method returns an iterable of the key-value pairs of the dictionary. The dict_items object is iterable

REFACTOR: make it better

  • I add another key-value pair to the dictionary to see what the method does when there is more than one

    127    def test_items(self):
    128        a_dictionary = {
    129            'key1': 'value1',
    130            'keyN': [0, 1, 2, 'n'],
    131        }
    132        self.assertEqual(
    133            list(a_dictionary.items()),
    134            [('key', 'value')]
    135        )
    136
    137
    138'items',
    

    the terminal shows AssertionError

    AssertionError: Lists differ: [('key1', 'value1'), ('keyN', [0, 1, 2, 'n'])] != [('key', 'value')]
    
  • I change the expectation to match

    132        self.assertEqual(
    133            list(a_dictionary.items()),
    134            [
    135                ('key1', 'value1'),
    136                ('keyN', [0, 1, 2, 'n']),
    137            ]
    138        )
    

    the test passes

  • I change the name of the test

    127    def test_items_returns_iterable_of_key_value_pairs_of_a_dictionary(self):
    128        a_dictionary = {
    129            'key1': 'value1',
    130            'keyN': [0, 1, 2, 'n'],
    131        }
    132        self.assertEqual(
    133            list(a_dictionary.items()),
    134            [
    135                ('key1', 'value1'),
    136                ('keyN', [0, 1, 2, 'n']),
    137            ]
    138        )
    139
    140
    141'items',
    
  • I remove items from the TODO list

    132        self.assertEqual(
    133            list(a_dictionary.items()),
    134            [
    135                ('key1', 'value1'),
    136                ('keyN', [0, 1, 2, 'n']),
    137            ]
    138        )
    139
    140
    141'keys',
    142'pop',
    143'popitem',
    144'setdefault',
    145'update',
    146'values'
    

    all tests are still passing

I know how to look at the key-value pairs in a dictionary as a list of tuples


test_keys_of_a_dictionary

RED: make it fail

I add a new test

132            self.assertEqual(
133                list(a_dictionary.items()),
134                [
135                    ('key1', 'value1'),
136                    ('keyN', [0, 1, 2, 'n']),
137                ]
138            )
139
140    def test_keys(self):
141        a_dictionary = {'key': 'value'}
142        self.assertIsNone(a_dictionary.keys())
143
144
145'keys',

the terminal shows AssertionError

AssertionError: dict_keys(['key']) is not None

this looks like the error in test_items_returns_iterable_of_key_value_pairs_of_a_dictionary

GREEN: make it pass

  • I copy the dict_keys object from the terminal and paste it as the expectation

    140    def test_keys(self):
    141        a_dictionary = {'key': 'value'}
    142        self.assertIsNone(
    143            a_dictionary.keys(),
    144            dict_keys(['key'])
    145        )
    

    the terminal shows NameError

    NameError: name 'dict_keys' is not defined
    

    the dict_keys object has a list

  • I use the list in the dict_keys object as the expectation

    140    def test_keys(self):
    141        a_dictionary = {'key': 'value'}
    142        self.assertIsNone(
    143            a_dictionary.keys(),
    144            ['key']
    145        )
    

    the terminal shows AssertionError

    AssertionError: dict_keys(['key']) is not None : ['key']
    
  • I pass the call to the keys method to list() to see if dict_keys is iterable

    142        self.assertIsNone(
    143            list(a_dictionary.keys()),
    144            ['key']
    145        )
    

    the terminal shows AssertionError

    AssertionError: ['key'] is not None : ['key']
    

    I change assertIsNone to assertEqual

    142        self.assertEqual(
    143            list(a_dictionary.keys()),
    144            ['key']
    145        )
    

    the test passes

REFACTOR: make it better

  • I add another key-value pair to the dictionary to see what the keys method returns when there are multiple

    140    def test_keys(self):
    141        a_dictionary = {
    142            'key1': 'value1',
    143            'keyN': [0, 1, 2, 'n'],
    144        }
    145        self.assertEqual(
    146            list(a_dictionary.keys()),
    147            ['key']
    148        )
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['key1', 'keyN'] != ['key']
    
  • I change the expectation to match

    145        self.assertEqual(
    146            list(a_dictionary.keys()),
    147            ['key1', 'keyN']
    148        )
    

    the test passes

  • I change the name of the test

    140    def test_keys_of_a_dictionary(self):
    141        a_dictionary = {
    142            'key1': 'value1',
    143            'keyN': [0, 1, 2, 'n'],
    144        }
    145        self.assertEqual(
    146            list(a_dictionary.keys()),
    147            ['key1', 'keyN']
    148        )
    149
    150
    151'keys',
    
  • I remove keys from the TODO list

    145        self.assertEqual(
    146            list(a_dictionary.keys()),
    147            ['key1', 'keyN']
    148        )
    149
    150
    151'pop',
    152'popitem',
    153'setdefault',
    154'update',
    155'values'
    

I know how to look at the keys in a dictionary


test_pop_removes_given_key_from_a_dictionary_and_returns_its_value

RED: make it fail

I wonder if the pop method of dictionaries behaves the same way as the pop method of lists. I add a test for it

145        self.assertEqual(
146            list(a_dictionary.keys()),
147            ['key1', 'keyN']
148        )
149
150    def test_pop(self):
151        a_dictionary = {'key': 'value'}
152        self.assertIsNone(a_dictionary.pop())
153
154
155'pop',

the terminal shows TypeError

TypeError: pop expected at least 1 argument, got 0

this pop method is different from the pop method of lists

GREEN: make it pass

  • I pass a value to the call

    152        self.assertIsNone(a_dictionary.pop(0))
    

    the terminal shows KeyError

    KeyError: 0
    
  • I add KeyError to the list of Exceptions seen

    162# Exceptions seen
    163# AssertionError
    164# TypeError
    165# NameError
    166# KeyError
    
  • I remove the things around the call in the test and change the value given to be clearer

    150    def test_pop(self):
    151        a_dictionary = {'key': 'value'}
    152        a_dictionary.pop('not_in_dictionary')
    

    the terminal shows KeyError

    KeyError: 'not in dictionary'
    
  • I add assertRaises

    150    def test_pop(self):
    151        a_dictionary = {'key': 'value'}
    152
    153        with self.assertRaises(KeyError):
    154            a_dictionary.pop('not_in_dictionary')
    

    the test passes. When I call the pop method with a key that is not in the dictionary it raises KeyError

REFACTOR: make it better

  • When I called the pop method without input, the terminal showed TypeError

    TypeError: pop expected at least 1 argument, got 0
    

    I add another assertion to see what happens when I call it with 2 arguments

    153        with self.assertRaises(KeyError):
    154            a_dictionary.pop('not_in_dictionary')
    155        self.assertIsNone(a_dictionary.pop('not_in_dictionary', None))
    

    the test is still green

  • I change the second argument, expecting a failure

    155        self.assertIsNone(a_dictionary.pop('not_in_dictionary', 'default'))
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    
  • I add the expectation

    155        self.assertIsNone(
    156            a_dictionary.pop('not_in_dictionary', 'default'),
    157            'default'
    158        )
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    
  • I change assertIsNone to assertEqual

    155        self.assertEqual(
    156            a_dictionary.pop('not_in_dictionary', 'default'),
    157            'default'
    158        )
    

    the test passes

  • I add another assertion to see what happens when I call the pop method with a key that is in the dictionary

    155        self.assertEqual(
    156            a_dictionary.pop('not_in_dictionary', 'default'),
    157            'default'
    158        )
    159        self.assertEqual(
    160            a_dictionary.pop('key', 'default'),
    161            'default'
    162        )
    

    the terminal shows AssertionError

    AssertionError: 'value' != 'default'
    

    I get 'value' back. The pop method returns the value of the given key from the dictionary

  • I change the expectation to match

    159        self.assertEqual(
    160            a_dictionary.pop('key', 'default'),
    161            'value'
    162        )
    

    the test passes

  • I add another assertion to see what the pop method did to the dictionary

    159        self.assertEqual(
    160            a_dictionary.pop('key', 'default'),
    161            'value'
    162        )
    163        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {} != {'key': 'value'}
    

    the pop method removes the key-value pair and returns the value of the given key from the dictionary

  • I change the expectation to match

    163        self.assertEqual(a_dictionary, {})
    

    the test passes

  • I change the name of the test

    150    def test_pop_removes_given_key_from_a_dictionary_and_returns_its_value(self):
    151        a_dictionary = {'key': 'value'}
    152
    153        with self.assertRaises(KeyError):
    154            a_dictionary.pop('not_in_dictionary')
    155        self.assertEqual(
    156            a_dictionary.pop('not_in_dictionary', 'default'),
    157            'default'
    158        )
    159        self.assertEqual(
    160            a_dictionary.pop('key', 'default'),
    161            'value'
    162        )
    163        self.assertEqual(a_dictionary, {})
    164
    165
    166'pop',
    
  • I remove pop from the TODO list

    163        self.assertEqual(a_dictionary, {})
    164
    165
    166'popitem',
    167'setdefault',
    168'update',
    169'values'
    

I know how to remove a key and its value from a dictionary


test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary

RED: make it fail

I add a failing test for the next item in the TODO list

163        self.assertEqual(a_dictionary, {})
164
165    def test_popitem(self):
166        a_dictionary = {'key': 'value'}
167        self.assertIsNone(a_dictionary.popitem())
168
169
170'popitem',

the terminal shows AssertionError

AssertionError: ('key', 'value') is not None

the popitem method returns the key-value pair in the dictionary as a tuple

GREEN: make it pass

I add the value from the terminal as the expectation

166        a_dictionary = {'key': 'value'}
167        self.assertIsNone(
168            a_dictionary.popitem(),
169            ('key', 'value')
170        )

the terminal shows AssertionError

AssertionError: ('key', 'value') is not None : ('key', 'value')

I change assertIsNone to assertEqual

167        self.assertEqual(
168            a_dictionary.popitem(),
169            ('key', 'value')
170        )

the test passes

REFACTOR: make it better

  • I add another assertion to see what the popitem method did to the dictionary

    167        self.assertEqual(
    168            a_dictionary.popitem(),
    169            ('key', 'value')
    170        )
    171        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {} != {'key': 'value'}
    

    popitem removes and returns the key-value pair from the dictionary

  • I change the value to match

    171        self.assertEqual(a_dictionary, {})
    

    the test passes

  • this operation does not take input, I change the dictionary to see what happens

    165    def test_popitem(self):
    166        a_dictionary = {
    167            'key1': 'value1',
    168            'keyN': [0, 1, 2, 'n'],
    169        }
    170        self.assertEqual(
    171            a_dictionary.popitem(),
    172            ('key', 'value')
    173        )
    174        self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: Tuples differ: ('keyN', [0, 1, 2, 'n']) != ('key', 'value')
    

    popitem removes and returns the last key-value pair from a dictionary

  • I change the expectation in the first assertEqual to match

    170        self.assertEqual(
    171            a_dictionary.popitem(),
    172            ('keyN', [0, 1, 2, 'n'])
    173        )
    

    the terminal shows AssertionError

    AssertionError: {'key1': 'value1'} != {'key': 'value'}
    
  • I change the value in the second assertEqual to match

    174        self.assertEqual(a_dictionary, {'key1': 'value1'})
    

    the test passes

  • I change the name of the test

    165    def test_popitem_removes_and_returns_last_key_value_pair_from_a_dictionary(self):
    166        a_dictionary = {
    167            'key1': 'value1',
    168            'keyN': [0, 1, 2, 'n'],
    169        }
    170        self.assertEqual(
    171            a_dictionary.popitem(),
    172            ('keyN', [0, 1, 2, 'n'])
    173        )
    174        self.assertEqual(a_dictionary, {'key1': 'value1'})
    175
    176
    177'popitem',
    
  • I remove popitem from the TODO list

    174        self.assertEqual(a_dictionary, {'key1': 'value1'})
    175
    176
    177'setdefault',
    178'update',
    179'values'
    

I know how to remove the last key-value pair from a dictionary


test_setdefault_adds_given_key_to_a_dictionary

RED: make it fail

I add a test for the setdefault method

174        self.assertEqual(a_dictionary, {'key1': 'value1'})
175
176    def test_setdefault(self):
177        a_dictionary = {'key': 'value'}
178        self.assertIsNone(a_dictionary.setdefault())
179
180
181'setdefault',

the terminal shows TypeError

TypeError: setdefault expected at least 1 argument, got 0

GREEN: make it pass

I pass a value in the call

178        self.assertIsNone(a_dictionary.setdefault(0))

the test passes

REFACTOR: make it better

  • The error message showed that the method expects at least 1 argument, I add a second argument

    178        self.assertIsNone(a_dictionary.setdefault(0, None))
    

    the test is still green

  • I change the second argument expecting a failure

    178        self.assertIsNone(a_dictionary.setdefault(0, 'default'))
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None
    
  • I add the expectation

    177        a_dictionary = {'key': 'value'}
    178        self.assertIsNone(
    179            a_dictionary.setdefault(0, 'default'),
    180            'default'
    181        )
    

    the terminal shows AssertionError

    AssertionError: 'default' is not None : default
    
  • I change assertIsNone to assertEqual

    178        self.assertEqual(
    179            a_dictionary.setdefault(0, 'default'),
    180            'default'
    181        )
    

    the test passes

  • I add an assertion to see what setdefault did to the dictionary

    178        self.assertEqual(
    179            a_dictionary.setdefault(0, 'default'),
    180            'default'
    181        )
    182        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 0: 'default'} != {'key': 'value'}
    

    setdefault adds the given key to the dictionary with the default value and returns the default value

  • I change the expectation to match

    176    def test_setdefault(self):
    177        a_dictionary = {'key': 'value'}
    178        self.assertEqual(
    179            a_dictionary.setdefault(0, 'default'),
    180            'default'
    181        )
    182        self.assertEqual(
    183            a_dictionary,
    184            {
    185                'key': 'value',
    186                0: 'default',
    187            }
    188        )
    

    the test passes

  • I change the 0 given in the call to the setdefault method be clearer

    178        self.assertEqual(
    179            a_dictionary.setdefault('new_key', 'default'),
    180            'default'
    181        )
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'new_key': 'default'} != {'key': 'value', 0: 'default'}
    
  • I change the expectation to match

    181        self.assertEqual(
    182            a_dictionary,
    183            {
    184                'key': 'value',
    185                'new_key': 'default',
    186            }
    187        )
    

    the test is green again

  • I add an assertion to see what happens when I use setdefault with a key that is already in the dictionary

    176    def test_setdefault(self):
    177        a_dictionary = {'key': 'value'}
    178        self.assertEqual(
    179            a_dictionary.setdefault('new_key', 'default'),
    180            'default'
    181        )
    182        self.assertEqual(
    183            a_dictionary.setdefault('key', 'default'),
    184            'default'
    185        )
    186        self.assertEqual(
    187            a_dictionary,
    188            {
    189                'key': 'value',
    190                'new_key': 'default',
    191            }
    192        )
    

    the terminal shows AssertionError

    AssertionError: 'value' != 'default'
    

    setdefault returns the value for a key in a dictionary when the key is in the dictionary

  • I change the expectation to match

    182        self.assertEqual(
    183            a_dictionary.setdefault('key', 'default'),
    184            'value'
    185        )
    

    the test passes

    Note

    setdefault has a condition

  • I change the name of the test

    176    def test_setdefault_adds_given_key_to_a_dictionary(self):
    177        a_dictionary = {'key': 'value'}
    178        self.assertEqual(
    179            a_dictionary.setdefault('new_key', 'default'),
    180            'default'
    181        )
    182        self.assertEqual(
    183            a_dictionary.setdefault('key', 'default'),
    184            'value'
    185        )
    186        self.assertEqual(
    187            a_dictionary,
    188            {
    189                'key': 'value',
    190                'new_key': 'default',
    191            }
    192        )
    193
    194
    195'setdefault',
    
  • I remove setdefault from the TODO list

    186        self.assertEqual(
    187            a_dictionary,
    188            {
    189                'key': 'value',
    190                'new_key': 'default',
    191            }
    192        )
    193
    194
    195'update',
    196'values'
    

I know how to add a key with a default value to a dictionary


test_update_a_dictionary

RED: make it fail

  • I add a test for the next method from the TODO list

    186        self.assertEqual(
    187            a_dictionary,
    188            {
    189                'key': 'value',
    190                'new_key': 'default',
    191            }
    192        )
    193
    194    def test_update(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update())
    197
    198
    199'update',
    

    the test is green. The update method returns None

  • I add an assertion to see what it did to the dictionary

    194    def test_update(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update())
    197        self.assertEqual(a_dictionary, {})
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value'} != {}
    

    the dictionary did not change

GREEN: make it pass

I change the values in the expectation to match the terminal

197        self.assertEqual(a_dictionary, {'key': 'value'})

the test passes

REFACTOR: make it better

  • I add a value to the call to the update method to see what happens

    194    def test_update(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update(0))
    197        self.assertEqual(a_dictionary, {'key': 'value'})
    

    the terminal shows TypeError

    TypeError: 'int' object is not iterable
    
  • I change the value to a tuple

    196        self.assertIsNone(a_dictionary.update((0, 1)))
    

    the terminal shows TypeError

    TypeError: cannot convert dictionary update sequence element #0 to a sequence
    

    I had the same error message when I tried to make a dictionary with things in it

  • I try a keyword argument

    196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
    

    the terminal shows AssertionError

    AssertionError: {'key': 'value', 'new_key': [0, 1, 2, 'n']} != {'key': 'value'}
    
  • I add the new key-value pair to the expectation

    196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
    197        self.assertEqual(
    198            a_dictionary,
    199            {
    200                'key': 'value',
    201                'new_key': [0, 1, 2, 'n'],
    202            }
    203        )
    

    the test passes

  • I add an assertion to see what happens when I give a key that is already in the dictionary

    194    def test_update(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
    197        self.assertIsNone(a_dictionary.update(key='updated value'))
    198        self.assertEqual(
    

    the terminal shows AssertionError

    AssertionError: {'key': 'updated value', 'new_key': [0, 1, 2, 'n']} != {'key': 'value', 'new_key': [0, 1, 2, 'n']}
    

    the update method changes the value for a key that is already in a dictionary

  • I change the expectation to match

    198        self.assertEqual(
    199            a_dictionary,
    200            {
    201                'key': 'updated value',
    202                'new_key': [0, 1, 2, 'n'],
    203            }
    204        )
    

    the test passes

  • since the update method takes keyword arguments, I can give it a dictionary as input. I add another assertion

    194    def test_update(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
    197        self.assertIsNone(a_dictionary.update(key='updated value'))
    198        self.assertIsNone(a_dictionary.update(
    199            {'another_key': {0, 1, 2, 'n'}}
    200        ))
    201        self.assertEqual(
    

    the terminal shows AssertionError

    AssertionError: {'key[14 chars]lue', 'new_key': [0, 1, 2, 'n'], 'another_key': {0, 1, 2, 'n'}} != {'key[14 chars]lue', 'new_key': [0, 1, 2, 'n']}
    

    I can use the update method to add key-value pairs from one dictionary to another

  • I change the expectation in the assertEqual to match

    201        self.assertEqual(
    202            a_dictionary,
    203            {
    204                'key': 'updated value',
    205                'new_key': [0, 1, 2, 'n'],
    206                'another_key': {0, 1, 2, 'n'},
    207            }
    208        )
    

    the test passes

  • I change the name of the test

    194    def test_update_a_dictionary(self):
    195        a_dictionary = {'key': 'value'}
    196        self.assertIsNone(a_dictionary.update(new_key=[0, 1, 2, 'n']))
    197        self.assertIsNone(a_dictionary.update(key='updated value'))
    198        self.assertIsNone(a_dictionary.update(
    199            {'another_key': {0, 1, 2, 'n'}}
    200        ))
    201        self.assertEqual(
    202            a_dictionary,
    203            {
    204                'key': 'updated value',
    205                'new_key': [0, 1, 2, 'n'],
    206                'another_key': {0, 1, 2, 'n'}
    207            }
    208        )
    209
    210
    211'update',
    

    the test is still green

  • I remove update from the TODO list

    201        self.assertEqual(
    202            a_dictionary,
    203            {
    204                'key': 'updated value',
    205                'new_key': [0, 1, 2, 'n'],
    206                'another_key': {0, 1, 2, 'n'}
    207            }
    208        )
    209
    210
    211'values'
    

I know how to add key-value pairs from one dictionary to another


test_values_of_a_dictionary

RED: make it fail

I add a test for the last method in my TODO list

201        self.assertEqual(
202            a_dictionary,
203            {
204                'key': 'updated value',
205                'new_key': [0, 1, 2, 'n'],
206                'another_key': {0, 1, 2, 'n'},
207            }
208        )
209
210    def test_values(self):
211        a_dictionary = {'key': 'value'}
212        self.assertIsNone(a_dictionary.values())
213
214
215'values'

the terminal shows AssertionError

AssertionError: dict_values(['value']) is not None

this is like test_items_returns_iterable_of_key_value_pairs_of_a_dictionary and test_keys_of_a_dictionary

GREEN: make it pass

  • I add the expected value

    210    def test_values(self):
    211        a_dictionary = {'key': 'value'}
    212        self.assertIsNone(
    213            a_dictionary.values,
    214            dict_values(['value'])
    215        )
    

    the terminal shows NameError

    NameError: name 'dict_values' is not defined
    
  • I remove the things around the list in the dict_values object

    212        self.assertIsNone(
    213            a_dictionary.values(),
    214            ['value']
    215        )
    

    the terminal shows AssertionError

    AssertionError: dict_values(['value']) is not None : ['value']
    
  • I use list() to see if dict_values is iterable

    212        self.assertIsNone(
    213            list(a_dictionary.values()),
    214            ['value']
    215        )
    

    the terminal shows AssertionError

    AssertionError: ['value'] is not None : ['value']
    
  • I change assertIsNone to assertEqual

    212        self.assertEqual(
    213            list(a_dictionary.values()),
    214            ['value']
    215        )
    

    the test passes

REFACTOR: make it better

  • I change the dictionary to see what happens when it has more than one key-value pair

    210    def test_values(self):
    211        a_dictionary = {
    212            'key1': 'value1',
    213            'keyN': [0, 1, 2, 'n'],
    214        }
    215        self.assertEqual(
    216            list(a_dictionary.values()),
    217            ['value']
    218        )
    

    the terminal shows AssertionError

    AssertionError: Lists differ: ['value1', [0, 1, 2, 'n']] != ['value']
    
  • I change the values in the expectation

    215        self.assertEqual(
    216            list(a_dictionary.values()),
    217            [
    218                'value1',
    219                [0, 1, 2, 'n'],
    220            ]
    221        )
    

    the test passes

  • I change the name of the test

    210    def test_values_of_a_dictionary(self):
    211        a_dictionary = {
    212            'key1': 'value1',
    213            'keyN': [0, 1, 2, 'n'],
    214        }
    215        self.assertEqual(
    216            list(a_dictionary.values()),
    217            [
    218                'value1',
    219                [0, 1, 2, 'n'],
    220            ]
    221        )
    222
    223
    224'values'
    
  • I remove values from the TODO list

    215        self.assertEqual(
    216            list(a_dictionary.values()),
    217            [
    218                'value1',
    219                [0, 1, 2, 'n']
    220            ]
    221        )
    222
    223
    224# Exceptions seen
    

I know how to look at the values of a dictionary


test_key_error

KeyError is an important Exception to know when working with a dictionary. It happened earlier in my test for the pop method

RED: make it fail

I add a test for getting the value of a key that is in a dictionary

215        self.assertEqual(
216            list(a_dictionary.values()),
217            [
218                'value1',
219                [0, 1, 2, 'n']
220            ]
221        )
222
223    def test_key_error(self):
224        a_dictionary = {'key': 'value'}
225        self.assertEqual(a_dictionary['key'], '')
226
227
228# Exceptions seen

the terminal shows AssertionError

AssertionError: 'value' != ''

I get 'value' back. I can get the value for a key in a dictionary by giving the key in [], the same way I can get items from a list by giving the index in []

GREEN: make it pass

I change the value in the expectation to match the terminal

225        self.assertEqual(a_dictionary['key'], 'value')

the test passes

REFACTOR: make it better

  • I add another assertion, this time for a key that is not in the dictionary

    225        self.assertEqual(a_dictionary['key'], 'value')
    226        self.assertEqual(a_dictionary['not_in_dictionary'])
    

    the terminal shows KeyError

    KeyError: 'not_in_dictionary'
    
  • I change the assertEqual to assertRaises

    225        self.assertEqual(a_dictionary['key'], 'value')
    226
    227        with self.assertRaises(KeyError):
    228            a_dictionary['not_in_dictionary']
    

    the test passes

  • I can use the get method when I do not want to get KeyError with a key that is not in a dictionary

    227        with self.assertRaises(KeyError):
    228            a_dictionary['not_in_dictionary']
    229        self.assertEqual(
    230            a_dictionary.get('not_in_dictionary', 'default'),
    231            ''
    232        )
    

    the terminal shows AssertionError

    AssertionError: 'default' != ''
    
  • I change the expectation

    229        self.assertEqual(
    230            a_dictionary.get('not_in_dictionary', 'default'),
    231            'default'
    232        )
    

    the test passes. This is a repetition of test_get_value_of_a_key_in_a_dictionary, here is another one

  • The pop method raised KeyError when I gave it a key that was not in the dictionary, I add a failing line for it

    229        self.assertEqual(
    230            a_dictionary.get('not_in_dictionary', 'default'),
    231            'default'
    232        )
    233
    234        a_dictionary.pop('not_in_dictionary')
    

    the terminal shows KeyError

    KeyError: 'not_in_dictionary'
    
  • I add assertRaises

    229        self.assertEqual(
    230            a_dictionary.get('not_in_dictionary', 'default'),
    231            'default'
    232        )
    233
    234        with self.assertRaises(KeyError):
    235            a_dictionary.pop('not_in_dictionary')
    

    the test passes

  • I can give a second argument when I do not want the pop method to raise KeyError when the key is not in the dictionary. I add an assertion

    234        with self.assertRaises(KeyError):
    235            a_dictionary.pop('not_in_dictionary')
    236        self.assertEqual(
    237            a_dictionary.pop('not_in_dictionary', 'default'),
    238            ''
    239        )
    

    the terminal shows AssertionError

    AssertionError: 'default' != ''
    
  • I change the expectation to match

    236        self.assertEqual(
    237            a_dictionary.pop('not_in_dictionary', 'default'),
    238            'default'
    239        )
    

    the test passes

  • The popitem method also raises KeyError when called on an empty dictionary

    236        self.assertEqual(
    237            a_dictionary.pop('not_in_dictionary', 'default'),
    238            'default'
    239        )
    240
    241        {}.popitem()
    

    the terminal shows KeyError

    KeyError: 'popitem(): dictionary is empty'
    

    I cannot remove the last key-value pair from a dictionary that has no key-value pairs

  • I add assertRaises

    223    def test_key_error(self):
    224        a_dictionary = {'key': 'value'}
    225        self.assertEqual(a_dictionary['key'], 'value')
    226
    227        with self.assertRaises(KeyError):
    228            a_dictionary['not_in_dictionary']
    229        self.assertEqual(
    230            a_dictionary.get('not_in_dictionary', 'default'),
    231            'default'
    232        )
    233
    234        with self.assertRaises(KeyError):
    235            a_dictionary.pop('not_in_dictionary')
    236        self.assertEqual(
    237            a_dictionary.pop('not_in_dictionary', 'default'),
    238            'default'
    239        )
    240
    241        with self.assertRaises(KeyError):
    242            {}.popitem()
    243
    244
    245# Exceptions seen
    246# AssertionError
    247# TypeError
    248# NameError
    249# KeyError
    

    the test passes

I know what causes KeyError


close the project

  • I close test_dictionaries.py in the editor

  • I click in the terminal and exit the tests with ctrl+c on the keyboard, the terminal shows

    .../pumping_python
    

    I am back in the pumping_python directory

Note

on Windows without Windows Subsystem for Linux

  • the terminal shows

    (.venv) ...\pumping_python\dictionaries
    
  • I deactivate the virtual environment

    deactivate
    

    the terminal goes back to the command line, (.venv) is no longer on the left side

    ...\pumping_python\dictionaries
    
  • I change directory to the parent of dictionaries

    cd ..
    

    the terminal shows

    ...\pumping_python
    

    I am back in the pumping_python directory


review

Dictionaries are also known as Mappings, they contain key-value pairs and any object can be used as values.

I ran tests to show that I can make a dictionary with dict() or curly braces {}, then I ran the following tests to see what Python basic data types I can use as keys in a dictionary

I also ran these tests for the methods of dictionaries

and a test for the important Exception to know when working with dictionaries - KeyError


How many questions can you answer after going through this chapter?


code from the chapter

Do you want to see all the CODE I typed in this chapter?


what is next?

you know

Would you like to use dictionaries in the calculator tests?


rate pumping python

If this has been a 7 star experience for you, please leave a 5 star review. It helps other people get into the book too