functions that take input
A function is code that is callable, which means I can write code to do something one time, and call the name for it to do that thing at a different time from when I write it.
functions can make code simpler, easier to read, test, reuse, maintain and improve - all the good things.
Part of Computer Programming is sending input data to a process and getting output data back
input_object -> process -> output_object
where process is the function. I think of it like mapping a function f in Mathematics with inputs x and output y
A function does something (the process) with input_object and returns output_object as the result. For example
f(x) -> y
function(input_object) -> output_object
solar_panel(sunlight) -> electricity
factory(materials) -> product
chef(ingredients) -> food
stomach(food) -> poop
how to make a function that takes input
functions that take input are made with
the def keyword
a name
parentheses with the inputs allowed
a colon after the parentheses
the code that makes up the function (its body) comes after the colon
def name_of_function(input_object):
the body of the function
return output_object
preview
I have these tests by the end of the chapter
1def assert_equal(input_1, input_2):
2 assert input_1 == input_2
3
4
5def assert_is_none(something):
6 assert something is None
7
8
9def test_making_a_function_w_pass():
10 def w_pass():
11 pass
12
13 assert_is_none(w_pass())
14
15
16def test_making_a_function_w_return():
17 def w_return():
18 return
19
20 assert_is_none(w_return())
23def test_making_a_function_w_return_none():
24 def w_return_none():
25 return None
26
27 assert_is_none(w_return_none())
28
29
30def test_what_happens_after_functions_return():
31 def return_leaves_the_function():
32 return None
33 return 'only one way for this line to run'
34
35 assert_is_none(return_leaves_the_function())
36
37
38def test_constant_function():
39 def constant():
40 return 'the same thing'
41
42 assert_equal(constant(), 'the same thing')
45def test_identity_function():
46 def identity(the_input):
47 return the_input
48
49 assert_is_none(identity(None))
50 assert_equal(identity(object), object)
51
52
53def test_why_use_a_function():
54 def add_x(number):
55 return 3 + number
56
57 assert_equal(add_x(0), 3)
58 assert_equal(add_x(1), 4)
59 assert_equal(add_x(2), 5)
60 assert_equal(add_x(3), 6)
61 assert_equal(add_x(4), 7)
62 assert_equal(add_x(5), 8)
63 assert_equal(add_x(6), 9)
64 assert_equal(add_x(7), 10)
65 assert_equal(add_x(8), 11)
66 assert_equal(add_x(9), 12)
69def positional_arguments(first_input, last_input):
70 return first_input, last_input
71
72
73def test_positional_arguments():
74 first, last = 'first', 'last'
75
76 assert_equal(
77 positional_arguments(first, last),
78 (first, last)
79 )
80 assert_equal(
81 positional_arguments(last, first),
82 (last, first)
83 )
84
85 assert_equal(
86 positional_arguments(0, 1), (0, 1)
87 )
88
89 a_tuple = (0, 1, 2, 'n')
90 a_list = [0, 1, 2, 'n']
91 assert_equal(
92 positional_arguments(a_tuple, a_list),
93 (a_tuple, a_list)
94 )
95
96 a_set = {0, 1, 2, 'n'}
97 a_dictionary = {'key': 'value'}
98 assert_equal(
99 keyword_arguments(
100 a_set, a_dictionary,
101 ),
102 (a_set, a_dictionary)
103 )
106def keyword_arguments(first_input, last_input):
107 return first_input, last_input
108
109
110def test_keyword_arguments():
111 first, last = 'first', 'last'
112
113 assert_equal(
114 keyword_arguments(
115 first_input=first, last_input=last,
116 ),
117 (first, last)
118 )
119 assert_equal(
120 keyword_arguments(
121 last_input=last, first_input=first,
122 ),
123 (first, last)
124 )
125
126 assert_equal(
127 keyword_arguments(
128 last_input=0, first_input=1,
129 ),
130 (1, 0)
131 )
132
133 a_tuple = (0, 1, 2, 'n')
134 a_list = [0, 1, 2, 'n']
135 assert_equal(
136 keyword_arguments(
137 first_input=a_tuple,
138 last_input=a_list,
139 ),
140 (a_tuple, a_list)
141 )
142
143 a_set = {0, 1, 2, 'n'}
144 a_dictionary = {'key': 'value'}
145 assert_equal(
146 positional_arguments(
147 last_input=a_dictionary,
148 first_input=a_set,
149 ),
150 (a_set, a_dictionary)
151 )
154def test_args_and_kwargs():
155 def args_and_kwargs(first_input, last_input):
156 return first_input, last_input
157
158 first, last = 'first', 'last'
159
160 assert_equal(
161 args_and_kwargs(
162 first, last_input=last
163 ),
164 (first, last)
165 )
168def test_optional_arguments():
169 def optional_arguments(
170 first_input, last_input='doe'
171 ):
172 return first_input, last_input
173
174 first_name, last_name = 'jane', 'doe'
175 assert_equal(
176 optional_arguments(
177 first_name,
178 ),
179 (first_name, last_name)
180 )
181
182 first_name, blow = 'joe', 'blow'
183 assert_equal(
184 optional_arguments(
185 first_name, blow
186 ),
187 (first_name, blow)
188 )
189
190 first_name = 'john'
191 assert_equal(
192 optional_arguments(
193 first_input=first_name,
194 ),
195 (first_name, last_name)
196 )
197
198 last_name = 'smith'
199 assert_equal(
200 optional_arguments(
201 last_input=last_name,
202 first_input=first_name,
203 ),
204 (first_name, last_name)
205 )
208def test_unknown_number_of_arguments():
209 def unknown_number_of_arguments(
210 *positional_arguments, **keyword_arguments
211 ):
212 return positional_arguments, keyword_arguments
213
214 a_tuple = (0, 1)
215 a_dictionary = {'a': 2, 'b': 3}
216 assert_equal(
217 unknown_number_of_arguments(
218 *a_tuple, **a_dictionary
219 ),
220 (a_tuple, a_dictionary)
221 )
222
223 a_dictionary = {'a': 2, 'b': 3, 'c': 4}
224 assert_equal(
225 unknown_number_of_arguments(
226 *a_tuple, **a_dictionary,
227 ),
228 (a_tuple, a_dictionary)
229 )
230
231 a_tuple = (0, 1, 2)
232 a_dictionary = {'a': 3, 'b': 4, 'c': 5}
233 assert_equal(
234 unknown_number_of_arguments(
235 *a_tuple, **a_dictionary
236 ),
237 (a_tuple, a_dictionary)
238 )
239
240 a_tuple = (0, 1, 2, 'n')
241 assert_equal(
242 unknown_number_of_arguments(*a_tuple),
243 (a_tuple, {})
244 )
245
246 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
247 assert_equal(
248 unknown_number_of_arguments(**a_dictionary),
249 ((), a_dictionary)
250 )
251
252 assert_equal(
253 unknown_number_of_arguments(), ((), {})
254 )
255
256
257# Exceptions seen
258# AssertionError
259# NameError
260# TypeError
261# SyntaxError
questions about functions that take input
open the project
I open a terminal
I change directory to the project
cd functionsthe terminal shows I am in the
functionsfolder.../pumping_python/functionsI open
test_functions.pyfrom thetestsfolderI use pytest-watcher to run the tests automatically
uv run pytest-watcher . --nowthe terminal shows
test_functions.py ..... [100%] =================== 5 passed in X.YZs ====================
test_identity_function
A function can take input and it returns None by default. The Identity or Passthrough function returns the input it gets as output.
RED: make it fail
I add a test to test_functions.py
30def test_constant_function():
31 def constant():
32 return 'the same thing'
33
34 assert constant() == 'the same thing'
35
36
37def test_identity_function():
38 assert identity() == None
39
40
41# Exceptions seen
the terminal is my friend, and shows NameError
NameError: name 'identity' is not defined
is it because test_functions.py has no identity?
GREEN: make it pass
I add a function for identity
37def test_identity_function():
38 def identity():
39 return None
40
41 assert identity() == None
42
43
44# Exceptions seen
the test passes because I get None when I call identity
identity() -> None
└── def identity():
└── return None
how to call a function with input
I can call a function with input by placing an object in parentheses (()) when I use the name after it is defined.
name_of_function(input_object)
RED: make it fail
I add input to the function call
37def test_identity_function():
38 def identity():
39 return None
40
41 # assert identity() == None
42 assert identity(None) == None
43
44
45# Exceptions seen
the terminal is my friend, and shows TypeError
TypeError:
test_identity_function.<locals>.identity()
takes 0 positional arguments but 1 was given
because
the call to
identitywhich belongs to test_identity_function uses one input (None).The function definition (signature) of
identitydoes not allow any inputs when it is called, since the parentheses are empty.The call to a function must match its signature (definition).
GREEN: make it pass
I add a name in parentheses for the identity function to take input
37def test_identity_function():
38 # def identity():
39 def identity(the_input):
40 return None
41
42 # assert identity() == None
43 assert identity(None) == None
44
45
46# Exceptions seen
the test passes. I am genius.
REFACTOR: make it better
The description for the identity function is that it returns the same thing it is given, this test passes when None is given as input.
Does it pass when another value is given or does it always return None? There is one way to find out
I add an assertion to test_identity_function in
37def test_identity_function(): 38 # def identity(): 39 def identity(the_input): 40 return None 41 42 # assert identity() == None 43 assert identity(None) == None 44 assert identity(object) == object 45 46 47# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert None == objectlooks like I am not all the way genius, yet. When I call
identityit returns None. Using substitution since I can treat a call to a function as the object it returnsassert identity(object) == object assert None == objectI get AssertionError since None is only equal to None.
object is the mother of everything in Python - everything in Python is an object (they inherit from it).
I make the
identityfunction return what it gets37def test_identity_function(): 38 # def identity(): 39 def identity(the_input): 40 # return None 41 return the_input 42 43 # assert identity() == None 44 assert identity(None) == None 45 assert identity(object) == object 46 47 48# Exceptions seenthe test passes.
identity(None ) -> None └── def identity(the_input): ├── the_input = None └── return the_inputidentity(object) -> object └── def identity(the_input): ├── the_input = object └── return the_inputI remove the commented lines
37def test_identity_function(): 38 def identity(the_input): 39 return the_input 40 41 assert identity(None) == None 42 assert identity(object) == object 43 44 45# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_identity_function'the terminal shows a summary of the changes then goes back to the command line.
I sometimes use the Identity Function when I am testing, to check connections. If I can send something (input) and get it back, I can start making changes to see how it affects the output.
test_why_use_a_function
Why would I use a function when I can just write code to do the thing I want? Let us assume I am writing a program to add up numbers.
RED: make it fail
I add a test
37def test_identity_function(): 38 def identity(the_input): 39 return the_input 40 41 assert identity(None) == None 42 assert identity(object) == object 43 44 45def test_why_use_a_function(): 46 assert 1 + 0 == 0 47 48 49# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 0) == 0because
1 + 0is NOT equal to0.
GREEN: make it pass
I change the assertion to make it True
45def test_why_use_a_function():
46 # assert 1 + 0 == 0
47 assert 1 + 0 == 1
48
49
50# Exceptions seen
the test passes.
REFACTOR: make it better
I add an assertion for
1 + 145def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 assert 1 + 1 == 1 49 50 51# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 1) == 1because
1 + 1is NOT equal to1.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 51 52# Exceptions seenthe test passes.
I add an assertion for
1 + 245def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 assert 1 + 2 == 2 51 52 53# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 2) == 2because
1 + 2is NOT equal to2.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 53 54# Exceptions seenthe test passes.
I add an assertion for
1 + 345def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 assert 1 + 3 == 3 53 54 55# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 3) == 3because
1 + 3is NOT equal to3.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 55 56# Exceptions seenthe test passes.
I add an assertion for
1 + 445def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 assert 1 + 4 == 4 55 56 57# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 4) == 4because
1 + 4is NOT equal to4.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 57 58# Exceptions seenthe test passes.
I add an assertion for
1 + 545def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 assert 1 + 5 == 5 57 58 59# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 5) == 5because
1 + 5is NOT equal to5.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 59 60# Exceptions seenthe test passes.
I add an assertion for
1 + 645def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 assert 1 + 6 == 6 59 60 61# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 6) == 6because
1 + 6is NOT equal to6.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 61 62# Exceptions seenthe test passes.
I add an assertion for
1 + 745def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 assert 1 + 7 == 7 61 62 63# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 7) == 7because
1 + 7is NOT equal to7.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 # assert 1 + 7 == 7 61 assert 1 + 7 == 8 62 63 64# Exceptions seenthe test passes.
I add an assertion for
1 + 845def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 # assert 1 + 7 == 7 61 assert 1 + 7 == 8 62 assert 1 + 8 == 8 63 64 65# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 8) == 8because
1 + 8is NOT equal to8.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 # assert 1 + 7 == 7 61 assert 1 + 7 == 8 62 # assert 1 + 8 == 8 63 assert 1 + 8 == 9 64 65 66# Exceptions seenthe test passes.
I add an assertion for
1 + 945def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 # assert 1 + 7 == 7 61 assert 1 + 7 == 8 62 # assert 1 + 8 == 8 63 assert 1 + 8 == 9 64 assert 1 + 9 == 9 65 66 67# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1 + 9) == 9because
1 + 9is NOT equal to9.I change the assertion to make it True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 assert 1 + 0 == 1 48 # assert 1 + 1 == 1 49 assert 1 + 1 == 2 50 # assert 1 + 2 == 2 51 assert 1 + 2 == 3 52 # assert 1 + 3 == 3 53 assert 1 + 3 == 4 54 # assert 1 + 4 == 4 55 assert 1 + 4 == 5 56 # assert 1 + 5 == 5 57 assert 1 + 5 == 6 58 # assert 1 + 6 == 6 59 assert 1 + 6 == 7 60 # assert 1 + 7 == 7 61 assert 1 + 7 == 8 62 # assert 1 + 8 == 8 63 assert 1 + 8 == 9 64 # assert 1 + 9 == 9 65 assert 1 + 9 == 10 66 67 68# Exceptions seenthe test passes.
all these assertions test what happens when I add a number to
1. If I want to test what happens when I add a number to2, I would have to change1in 10 places. I change1to2for the calculation part of the assertions45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 # assert 1 + 0 == 1 48 assert 2 + 0 == 1 49 # assert 1 + 1 == 1 50 # assert 1 + 1 == 2 51 assert 2 + 1 == 2 52 # assert 1 + 2 == 2 53 # assert 1 + 2 == 3 54 assert 2 + 2 == 3 55 # assert 1 + 3 == 3 56 # assert 1 + 3 == 4 57 assert 2 + 3 == 4 58 # assert 1 + 4 == 4 59 # assert 1 + 4 == 5 60 assert 2 + 4 == 5 61 # assert 1 + 5 == 5 62 # assert 1 + 5 == 6 63 assert 2 + 5 == 6 64 # assert 1 + 6 == 6 65 # assert 1 + 6 == 7 66 assert 2 + 6 == 7 67 # assert 1 + 7 == 7 68 # assert 1 + 7 == 8 69 assert 2 + 7 == 8 70 # assert 1 + 8 == 8 71 # assert 1 + 8 == 9 72 assert 2 + 8 == 9 73 # assert 1 + 9 == 9 74 # assert 1 + 9 == 10 75 assert 2 + 9 == 10 76 77 78# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (2 + 0) == 1I change the result side of each assertion to make them True
45def test_why_use_a_function(): 46 # assert 1 + 0 == 0 47 # assert 1 + 0 == 1 48 # assert 2 + 0 == 1 49 assert 2 + 0 == 2 50 # assert 1 + 1 == 1 51 # assert 1 + 1 == 2 52 # assert 2 + 1 == 2 53 assert 2 + 1 == 3 54 # assert 1 + 2 == 2 55 # assert 1 + 2 == 3 56 # assert 2 + 2 == 3 57 assert 2 + 2 == 4 58 # assert 1 + 3 == 3 59 # assert 1 + 3 == 4 60 # assert 2 + 3 == 4 61 assert 2 + 3 == 5 62 # assert 1 + 4 == 4 63 # assert 1 + 4 == 5 64 # assert 2 + 4 == 5 65 assert 2 + 4 == 6 66 # assert 1 + 5 == 5 67 # assert 1 + 5 == 6 68 # assert 2 + 5 == 6 69 assert 2 + 5 == 7 70 # assert 1 + 6 == 6 71 # assert 1 + 6 == 7 72 # assert 2 + 6 == 7 73 assert 2 + 6 == 8 74 # assert 1 + 7 == 7 75 # assert 1 + 7 == 8 76 # assert 2 + 7 == 8 77 assert 2 + 7 == 9 78 # assert 1 + 8 == 8 79 # assert 1 + 8 == 9 80 # assert 2 + 8 == 9 81 assert 2 + 8 == 10 82 # assert 1 + 9 == 9 83 # assert 1 + 9 == 10 84 # assert 2 + 9 == 10 85 assert 2 + 9 == 11 86 87 88# Exceptions seenthe test passes.
I add a git commit message
git commit --all --message \ 'add test_why_use_a_function'the terminal shows a summary of the changes then goes back to the command line.
I go back to the terminal where the tests are running
What if I want to test what happens when I add
3to a number? Wait! No more, please! I do not want to have to make a change for each new number, there has to be a better way. I can use a function for the parts that repeat. I add a function to test_why_use_a_function45def test_why_use_a_function(): 46 def add_x(number): 47 return 2 + number 48 49 # assert 1 + 0 == 0 50 # assert 1 + 0 == 1 51 # assert 2 + 0 == 1 52 assert 2 + 0 == 2I use the new function for the calculation in the first assertion
45def test_why_use_a_function(): 46 def add_x(number): 47 return 2 + number 48 49 # assert 1 + 0 == 0 50 # assert 1 + 0 == 1 51 # assert 2 + 0 == 1 52 # assert 2 + 0 == 2 53 assert add_x(0) == 2 54 # assert 1 + 1 == 1 55 # assert 1 + 1 == 2 56 # assert 2 + 1 == 2 57 assert 2 + 1 == 3the test is still green because when I call
add_xwith a number as input, it returns2plus the number as output.add_x(number) -> 2 + number └── def add_x(number): └── return 2 + numberWhen
add_x(0)runsadd_x(0) -> 2 └── def add_x(number): ├── number = 0 └── return 2 + number return 2 + 0 return 2Using substitution since I can treat a call to a function as the object it returns
assert add_x(0) == 2 assert 2 == 22 + 0is equal to2.I use the
add_xfunction for the other assertions45def test_why_use_a_function(): 46 def add_x(number): 47 return 2 + number 48 49 # assert 1 + 0 == 0 50 # assert 1 + 0 == 1 51 # assert 2 + 0 == 1 52 # assert 2 + 0 == 2 53 assert add_x(0) == 2 54 # assert 1 + 1 == 1 55 # assert 1 + 1 == 2 56 # assert 2 + 1 == 2 57 # assert 2 + 1 == 3 58 assert add_x(1) == 3 59 # assert 1 + 2 == 2 60 # assert 1 + 2 == 3 61 # assert 2 + 2 == 3 62 # assert 2 + 2 == 4 63 assert add_x(2) == 4 64 # assert 1 + 3 == 3 65 # assert 1 + 3 == 4 66 # assert 2 + 3 == 4 67 # assert 2 + 3 == 5 68 assert add_x(3) == 5 69 # assert 1 + 4 == 4 70 # assert 1 + 4 == 5 71 # assert 2 + 4 == 5 72 # assert 2 + 4 == 6 73 assert add_x(4) == 6 74 # assert 1 + 5 == 5 75 # assert 1 + 5 == 6 76 # assert 2 + 5 == 6 77 # assert 2 + 5 == 7 78 assert add_x(5) == 7 79 # assert 1 + 6 == 6 80 # assert 1 + 6 == 7 81 # assert 2 + 6 == 7 82 # assert 2 + 6 == 8 83 assert add_x(6) == 8 84 # assert 1 + 7 == 7 85 # assert 1 + 7 == 8 86 # assert 2 + 7 == 8 87 # assert 2 + 7 == 9 88 assert add_x(7) == 9 89 # assert 1 + 8 == 8 90 # assert 1 + 8 == 9 91 # assert 2 + 8 == 9 92 # assert 2 + 8 == 10 93 assert add_x(8) == 10 94 # assert 1 + 9 == 9 95 # assert 1 + 9 == 10 96 # assert 2 + 9 == 10 97 # assert 2 + 9 == 11 98 assert add_x(9) == 11 99 100 101# Exceptions seenstill green.
Now I only have to make a change in one place if I want to test what happens if I add
3to a number45def test_why_use_a_function(): 46 def add_x(number): 47 # return 2 + number 48 return 3 + number 49 50 # assert 1 + 0 == 0 51 # assert 1 + 0 == 1 52 # assert 2 + 0 == 1 53 # assert 2 + 0 == 2 54 assert add_x(0) == 2the terminal is my friend, and shows AssertionError
E assert 3 == 2because
add_x(number) -> 3 + number └── def add_x(number): └── return 3 + numberI change the results part of the assertions one at a time
45def test_why_use_a_function(): 46 def add_x(number): 47 # return 2 + number 48 return 3 + number 49 50 # assert 1 + 0 == 0 51 # assert 1 + 0 == 1 52 # assert 2 + 0 == 1 53 # assert 2 + 0 == 2 54 # assert add_x(0) == 2 55 assert add_x(0) == 3 56 # assert 1 + 1 == 1 57 # assert 1 + 1 == 2 58 # assert 2 + 1 == 2 59 # assert 2 + 1 == 3 60 # assert add_x(1) == 3 61 assert add_x(1) == 4 62 # assert 1 + 2 == 2 63 # assert 1 + 2 == 3 64 # assert 2 + 2 == 3 65 # assert 2 + 2 == 4 66 # assert add_x(2) == 4 67 assert add_x(2) == 5 68 # assert 1 + 3 == 3 69 # assert 1 + 3 == 4 70 # assert 2 + 3 == 4 71 # assert 2 + 3 == 5 72 # assert add_x(3) == 5 73 assert add_x(3) == 6 74 # assert 1 + 4 == 4 75 # assert 1 + 4 == 5 76 # assert 2 + 4 == 5 77 # assert 2 + 4 == 6 78 # assert add_x(4) == 6 79 assert add_x(4) == 7 80 # assert 1 + 5 == 5 81 # assert 1 + 5 == 6 82 # assert 2 + 5 == 6 83 # assert 2 + 5 == 7 84 # assert add_x(5) == 7 85 assert add_x(5) == 8 86 # assert 1 + 6 == 6 87 # assert 1 + 6 == 7 88 # assert 2 + 6 == 7 89 # assert 2 + 6 == 8 90 # assert add_x(6) == 8 91 assert add_x(6) == 9 92 # assert 1 + 7 == 7 93 # assert 1 + 7 == 8 94 # assert 2 + 7 == 8 95 # assert 2 + 7 == 9 96 # assert add_x(7) == 9 97 assert add_x(7) == 10 98 # assert 1 + 8 == 8 99 # assert 1 + 8 == 9 100 # assert 2 + 8 == 9 101 # assert 2 + 8 == 10 102 # assert add_x(8) == 10 103 assert add_x(8) == 11 104 # assert 1 + 9 == 9 105 # assert 1 + 9 == 10 106 # assert 2 + 9 == 10 107 # assert 2 + 9 == 11 108 # assert add_x(9) == 11 109 assert add_x(9) == 12 110 111 112# Exceptions seenthe test passes.
I remove the commented lines
45def test_why_use_a_function(): 46 def add_x(number): 47 return 3 + number 48 49 assert add_x(0) == 3 50 assert add_x(1) == 4 51 assert add_x(2) == 5 52 assert add_x(3) == 6 53 assert add_x(4) == 7 54 assert add_x(5) == 8 55 assert add_x(6) == 9 56 assert add_x(7) == 10 57 assert add_x(8) == 11 58 assert add_x(9) == 12 59 60 61# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'extract add_x function'the terminal shows a summary of the changes then goes back to the command line.
test_identity_function used one input, these next tests use functions that take more than one input.
test_positional_arguments
I can call functions with positional arguments.
RED: make it fail
I go back to the terminal where the tests are running
I add test_positional_arguments
58 assert add_x(9) == 12 59 60 61def test_positional_arguments(): 62 assert positional_arguments() == None 63 64 65# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'positional_arguments' is not definedbecause …
GREEN: make it pass
I add the function
61def test_positional_arguments():
62 def positional_arguments():
63 return None
64
65 assert positional_arguments() == None
66
67
68# Exceptions seen
the test passes.
positional_arguments() -> None
└── def positional_arguments():
└── return None
REFACTOR: make it better
I add input to the function call
61def test_positional_arguments(): 62 def positional_arguments(): 63 return None 64 65 # assert positional_arguments() == None 66 assert positional_arguments('first') == None 67 68 69# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_positional_arguments.<locals>.positional_arguments() takes 0 positional arguments but 1 was givenbecause
the call to
positional_argumentswhich belongs to test_positional_arguments uses one input ('first').the function definition (signature) of
positional_argumentsdoes not allow any inputs when it is called since the parentheses are empty.the call to a function must match its signature (definition).
I add a name in parentheses to make the function take input
61def test_positional_arguments(): 62 # def positional_arguments(): 63 def positional_arguments(the_input): 64 return None 65 66 # assert positional_arguments() == None 67 assert positional_arguments('first') == None 68 69 70# Exceptions seenthe test passes because
positional_arguments(the_input) -> None └── def positional_arguments(the_input): └── return NoneI add another input to the function call
61def test_positional_arguments(): 62 # def positional_arguments(): 63 def positional_arguments(the_input): 64 return None 65 66 # assert positional_arguments() == None 67 # assert positional_arguments('first') == None 68 assert positional_arguments('first', 'last') == None 69 70 71# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_positional_arguments.<locals>.positional_arguments() takes 1 positional arguments but 2 was givenbecause
the call to
positional_argumentswhich belongs to test_positional_arguments uses two inputs('first'` and ``'last').the function definition (signature) of
positional_argumentsonly allows one input.the call to a function must match its signature (definition).
I make the function take two inputs by changing the name of the first input to be clearer, then I add a another name to the parentheses
61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 return None 66 67 # assert positional_arguments() == None 68 # assert positional_arguments('first') == None 69 assert positional_arguments('first', 'last') == None 70 71 72# Exceptions seenthe test passes.
I change the expectation of the assertion
65def test_positional_arguments(): 66 # def positional_arguments(): 67 # def positional_arguments(the_input): 68 def positional_arguments(first_input, last_input): 69 return None 70 71 # assert positional_arguments() == None 72 # assert positional_arguments('first') == None 73 # assert positional_arguments('first', 'last') == None 74 assert ( 75 positional_arguments('first', 'last') 76 == ('first', 'last') 77 ) 78 79 80# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert None == ('first', 'last')because when I call
positional_argumentswith'first'and'last'as inputs, it returns Nonepositional_arguments(first_input, last_input) -> None └── def positional_arguments(first_input, last_input): └── return NoneUsing substitution since I can treat a call to a function as the object it returns
assert positional_arguments('first', 'last') == ('first', 'last') assert None == ('first', 'last')I get AssertionError since None is NOT equal to a tuple.
I change the return statement to make the function return its inputs as output (like the identity function)
61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 # return None 66 return first_input, last_input 67 68 # assert positional_arguments() == None 69 # assert positional_arguments('first') == None 70 # assert positional_arguments('first', 'last') == None 71 assert ( 72 positional_arguments('first', 'last') 73 == ('first', 'last') 74 ) 75 76 77# Exceptions seenthe test passes, because the function always returns
first_input, last_inputand the call in the test sends'first'asfirst_inputand'last'aslast_inputWhen
positional_arguments('first', 'last')runspositional_arguments('first', 'last') -> ('first', 'last') └── def positional_arguments(first_input, last_input) ├── first_input = 'first' ├── last_input = 'last' └── return first_input, last_input return 'first' , 'last'The bad thing about giving arguments this way, is I must use the exact same order in the function definition when I make a call a function or I get something different. The good thing about giving arguments this way is I do not need to know the names of the arguments. I add an assertion to show this
61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 # return None 66 return first_input, last_input 67 68 # assert positional_arguments() == None 69 # assert positional_arguments('first') == None 70 # assert positional_arguments('first', 'last') == None 71 assert ( 72 positional_arguments('first', 'last') 73 == ('first', 'last') 74 ) 75 assert ( 76 positional_arguments('last', 'first') 77 == ('first', 'last') 78 ) 79 80 81# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('last', 'first') == ('first', 'last')because the function always returns
first_input, last_inputand this test calls the function with'last'asfirst_inputand'first'aslast_input.When
positional_arguments('last', 'first')runspositional_arguments('last', 'first') -> ('last', 'first') └── def positional_arguments(first_input, last_input) ├── first_input = 'last' ├── last_input = 'first' └── return first_input, last_input return 'last' , 'first'Using substitution since I can treat a call to a function as the object it returns
assert positional_arguments('last', 'first') == ('first', 'last') assert ('last', 'first') == ('first', 'last')I change my expectation to match reality
61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 # return None 66 return first_input, last_input 67 68 # assert positional_arguments() == None 69 # assert positional_arguments('first') == None 70 # assert positional_arguments('first', 'last') == None 71 assert ( 72 positional_arguments('first', 'last') 73 == ('first', 'last') 74 ) 75 assert ( 76 positional_arguments('last', 'first') 77 # == ('first', 'last') 78 == ('last', 'first') 79 ) 80 81 82# Exceptions seenthe test passes.
I add variables for
'first'and'last'in test_positional_arguments61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 # return None 66 return first_input, last_input 67 68 # assert positional_arguments() == None 69 # assert positional_arguments('first') == None 70 # assert positional_arguments('first', 'last') == None 71 72 first, last = 'first', 'last' 73 74 assert ( 75 positional_arguments('first', 'last') 76 == ('first', 'last') 77 ) 78 assert ( 79 positional_arguments('last', 'first') 80 # == ('first', 'last') 81 == ('last', 'first') 82 ) 83 84 85# Exceptions seenI use the variables to remove repetition of
'first'and'last'from test_positional_arguments61def test_positional_arguments(): 62 # def positional_arguments(): 63 # def positional_arguments(the_input): 64 def positional_arguments(first_input, last_input): 65 # return None 66 return first_input, last_input 67 68 # assert positional_arguments() == None 69 # assert positional_arguments('first') == None 70 # assert positional_arguments('first', 'last') == None 71 72 first, last = 'first', 'last' 73 74 assert ( 75 # positional_arguments('first', 'last') 76 # == ('first', 'last') 77 positional_arguments(first, last) 78 == (first, last) 79 ) 80 assert ( 81 # positional_arguments('last', 'first') 82 # == ('first', 'last') 83 # == ('last', 'first') 84 positional_arguments(last, first) 85 == (last, first) 86 ) 87 88 89# Exceptions seenthe test is still green.
I add another assertion to test_positional_arguments
80 assert ( 81 # positional_arguments('last', 'first') 82 # == ('first', 'last') 83 # == ('last', 'first') 84 positional_arguments(last, first) 85 == (last, first) 86 ) 87 88 assert ( 89 positional_arguments(0, 1) 90 == (1, 0) 91 ) 92 93 94# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (0, 1) == (1, 0)because the function always returns
first_input, last_inputand this test calls the function with0asfirst_inputand1aslast_input.Using substitution since I can treat a call to a function as the object it returns
assert positional_arguments(0, 1) == (1, 0) assert (0, 1) == (1, 0)I change my expectation to match reality
88 assert ( 89 positional_arguments(0, 1) 90 # == (1, 0) 91 == (0, 1) 92 ) 93 94 95# Exceptions seenthe test passes.
positional_arguments(0, 1) -> (0, 1) └── def positional_arguments(first_input, last_input) ├── first_input = 0 ├── last_input = 1 └── return first_input, last_input return 0 , 1I add an assertion to test_positional_arguments with a tuple (anything in parentheses
( )separated by a comma) and a list (anything in square brackets[ ])88 assert ( 89 positional_arguments(0, 1) 90 # == (1, 0) 91 == (0, 1) 92 ) 93 94 a_tuple = (0, 1, 2, 'n') 95 a_list = [0, 1, 2, 'n'] 96 assert ( 97 positional_arguments(a_list, a_tuple) 98 == (a_tuple, a_list) 99 ) 100 101 102# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ([1, 2, 3, 'n...0, 1, 2, 'n')) == ((1, 2, 3, 'n...0, 1, 2, 'n'])because the function always returns
first_input, last_inputand the call in this test sends(0, 1, 2, 'n')asfirst_inputand[0, 1, 2, 'n']aslast_input.Using substitution
assert positional_arguments(a_list, a_tuple) == (a_tuple, a_list) assert ([0, 1, 2, 'n'], (0, 1, 2, 'n')) == ((0, 1, 2, 'n'), [0, 1, 2, 'n'])I change reality to match my expectation
93 a_tuple = (0, 1, 2, 'n') 94 a_list = [0, 1, 2, 'n'] 95 assert ( 96 # positional_arguments(a_list, a_tuple) 97 positional_arguments(a_tuple, a_list) 98 == (a_tuple, a_list) 99 ) 100 101 102# Exceptions seenthe test passes.
├── a_tuple = (0, 1, 2, 'n') ├── a_list = [0, 1, 2, 'n'] └── positional_arguments(a_tuple, a_list) -> (a_tuple, a_list) └── def positional_arguments(first_input, last_input) ├── first_input = a_tuple ├── last_input = a_list └── return first_input, last_input return a_tuple , a_listI remove the commented lines
61def test_positional_arguments(): 62 def positional_arguments(first_input, last_input): 63 return first_input, last_input 64 65 first, last = 'first', 'last' 66 67 assert ( 68 positional_arguments(first, last) 69 == (first, last) 70 ) 71 assert ( 72 positional_arguments(last, first) 73 == (last, first) 74 ) 75 76 assert ( 77 positional_arguments(0, 1) 78 == (0, 1) 79 ) 80 81 a_tuple = (0, 1, 2, 'n') 82 a_list = [0, 1, 2, 'n'] 83 assert ( 84 positional_arguments(a_tuple, a_list) 85 == (a_tuple, a_list) 86 ) 87 88 89# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_positional_arguments'the terminal shows a summary of the changes then goes back to the command line.
extract assert_equal function
The assertions in test_positional_arguments, test_why_use_a_function, test_identity_function and test_constant_function are the same, they check if the result of a function call is equal to something.
assert function() == something
I can use a function to assert if two things are equal.
RED: make it fail
I go back to the terminal where the tests are running
I add a function named
assert_equalthat takes two inputs and asserts that they are equal1def assert_equal(input_1, input_2): 2 assert input_1 == input_2 3 4 5def test_making_a_function_w_pass():I use the new function for the first assertion in test_positional_arguments
65def test_positional_arguments(): 66 def positional_arguments(first_input, last_input): 67 return first_input, last_input 68 69 first, last = 'first', 'last' 70 71 # assert ( 72 # positional_arguments(first, last) 73 # == (first, last) 74 # ) 75 assert_equal( 76 positional_arguments(first, last), 77 (last, first) 78 ) 79 assert ( 80 positional_arguments(last, first) 81 == (last, first) 82 )the terminal is my friend, and shows AssertionError
AssertionError: assert ('first', 'last') == ('last', 'first')
GREEN: make it pass
I change the expectation to match reality
75 assert_equal(
76 positional_arguments(first, last),
77 # (last, first)
78 (first, last)
79 )
80 assert (
81 positional_arguments(last, first)
82 == (last, first)
83 )
the test passes.
├── first = 'first'
├── last = 'last'
└── assert_equal(
positional_arguments(first, last),
(first, last)
) -> None
└── def assert_equal(input_1, input_2):
├── input_1 = positional_arguments(first, last)
│ └── def positional_arguments(
│ first_input, last_input
│ ):
│ ├── first_input = first
│ ├── last_input = last
│ └── return first_input, last_input
│ return first , last
├── input_2 = (first, last)
└── assert input_1 == input_2
assert (first, last) == (first, last)
REFACTOR: make it better
I use the assert_equal function for the second assertion in test_positional_arguments
80 # assert ( 81 # positional_arguments(last, first) 82 # == (last, first) 83 # ) 84 assert_equal( 85 positional_arguments(last, first), 86 (first, last) 87 ) 88 89 assert ( 90 positional_arguments(0, 1) 91 == (0, 1) 92 )the terminal is my friend, and shows AssertionError
AssertionError: assert ('last', 'first') == ('first', 'last')I change the expectation to match reality
84 assert_equal( 85 positional_arguments(last, first), 86 # (first, last) 87 (last, first) 88 ) 89 90 assert ( 91 positional_arguments(0, 1) 92 == (0, 1) 93 )the test passes.
├── first = 'first' ├── last = 'last' └── assert_equal( positional_arguments(last, first), (last, first) ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = positional_arguments(last, first) │ └── def positional_arguments( │ first_input, last_input │ ): │ ├── first_input = last │ ├── last_input = first │ └── return first_input, last_input │ return last , first ├── input_2 = (last, first) └── assert input_1 == input_2 assert (last, first) == (last, first)I call the assert_equal function for the third assertion in test_positional_arguments
90 # assert ( 91 # positional_arguments(0, 1) 92 # == (0, 1) 93 # ) 94 assert_equal( 95 positional_arguments(0, 1), (1, 0) 96 ) 97 98 a_tuple = (0, 1, 2, 'n')the terminal is my friend, and shows AssertionError
E assert (0, 1) == (1, 0)I change the expectation to match reality for the third assertion
94 assert_equal( 95 # positional_arguments(0, 1), (1, 0) 96 positional_arguments(0, 1), (0, 1) 97 ) 98 99 a_tuple = (0, 1, 2, 'n')the test passes.
assert_equal( positional_arguments(0, 1), (0, 1) ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = positional_arguments(0, 1) │ └── def positional_arguments( │ first_input, last_input │ ): │ ├── first_input = 0 │ ├── last_input = 1 │ └── return first_input, last_input │ return 0 , 1 ├── input_2 = (0, 1) └── assert input_1 == input_2 assert (0, 1) == (0, 1)I use the assert_equal function for the fourth assertion in test_positional_arguments
99 a_tuple = (0, 1, 2, 'n') 100 a_list = [0, 1, 2, 'n'] 101 # assert ( 102 # positional_arguments(a_tuple, a_list) 103 # == (a_tuple, a_list) 104 # ) 105 assert_equal( 106 positional_arguments(a_list, a_tuple), 107 (a_tuple, a_list) 108 ) 109 110 111# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ([0, 1, 2, 'n...0, 1, 2, 'n')) == ((0, 1, 2, 'n...0, 1, 2, 'n']I change the call to
positional_argumentsto match the expectation of the fourth assertion in test_positional_arguments99 a_tuple = (0, 1, 2, 'n') 100 a_list = [0, 1, 2, 'n'] 101 # assert ( 102 # positional_arguments(a_tuple, a_list) 103 # == (a_tuple, a_list) 104 # ) 105 assert_equal( 106 # positional_arguments(a_list, a_tuple), 107 positional_arguments(a_tuple, a_list), 108 (a_tuple, a_list) 109 ) 110 111 112# Exceptions seenthe test passes.
├── a_tuple = (0, 1, 2, 'n') ├── a_list = [0, 1, 2, 'n'] └── assert_equal( positional_arguments(a_tuple, a_list), (a_tuple, a_list) ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = positional_arguments(a_tuple, a_list) │ └── def positional_arguments( │ first_input, last_input │ ): │ ├── first_input = a_tuple │ ├── last_input = a_list │ └── return first_input, last_input │ return a_tuple , a_list ├── input_2 = (a_tuple, a_list) └── assert input_1 == input_2 assert (a_tuple, a_list) == (a_tuple, a_list)I remove the commented lines from test_positional_arguments
65def test_positional_arguments(): 66 def positional_arguments(first_input, last_input): 67 return first_input, last_input 68 69 first, last = 'first', 'last' 70 71 assert_equal( 72 positional_arguments(first, last), 73 (first, last) 74 ) 75 assert_equal( 76 positional_arguments(last, first), 77 (last, first) 78 )80 assert_equal( 81 positional_arguments(0, 1), (0, 1) 82 ) 83 84 a_tuple = (0, 1, 2, 'n') 85 a_list = [0, 1, 2, 'n'] 86 assert_equal( 87 positional_arguments(a_tuple, a_list), 88 (a_tuple, a_list) 89 ) 90 91 92# ExceptionsI use the assert_equal function for the assertions in test_why_use_a_function
49def test_why_use_a_function(): 50 def add_x(number): 51 return 3 + number 52 53 # assert add_x(0) == 3 54 assert_equal(add_x(0), 2) 55 # assert add_x(1) == 4 56 assert_equal(add_x(1), 3) 57 # assert add_x(2) == 5 58 assert_equal(add_x(2), 4) 59 # assert add_x(3) == 6 60 assert_equal(add_x(3), 5) 61 # assert add_x(4) == 7 62 assert_equal(add_x(4), 6) 63 # assert add_x(5) == 8 64 assert_equal(add_x(5), 7) 65 # assert add_x(6) == 9 66 assert_equal(add_x(6), 8) 67 # assert add_x(7) == 10 68 assert_equal(add_x(7), 9) 69 # assert add_x(8) == 11 70 assert_equal(add_x(8), 10) 71 # assert add_x(9) == 12 72 assert_equal(add_x(9), 11) 73 74 75def test_positional_arguments():the terminal is my friend, and shows AssertionError
E assert 3 == 2I change the expectations of the assertions of test_why_use_a_function
49def test_why_use_a_function(): 50 def add_x(number): 51 return 3 + number 52 53 # assert add_x(0) == 3 54 # assert_equal(add_x(0), 2) 55 assert_equal(add_x(0), 3) 56 # assert add_x(1) == 4 57 # assert_equal(add_x(1), 3) 58 assert_equal(add_x(1), 4) 59 # assert add_x(2) == 5 60 # assert_equal(add_x(2), 4) 61 assert_equal(add_x(2), 5) 62 # assert add_x(3) == 6 63 # assert_equal(add_x(3), 5) 64 assert_equal(add_x(3), 6) 65 # assert add_x(4) == 7 66 # assert_equal(add_x(4), 6) 67 assert_equal(add_x(4), 7) 68 # assert add_x(5) == 8 69 # assert_equal(add_x(5), 7) 70 assert_equal(add_x(5), 8) 71 # assert add_x(6) == 9 72 # assert_equal(add_x(6), 8) 73 assert_equal(add_x(6), 9) 74 # assert add_x(7) == 10 75 # assert_equal(add_x(7), 9) 76 assert_equal(add_x(7), 10) 77 # assert add_x(8) == 11 78 # assert_equal(add_x(8), 10) 79 assert_equal(add_x(8), 11) 80 # assert add_x(9) == 12 81 # assert_equal(add_x(9), 11) 82 assert_equal(add_x(9), 12) 83 84 85def test_positional_arguments():the test passes.
assert_equal(add_x(0), 3) -> None └── def assert_equal(input_1, input_2): ├── input_1 = add_x(0) │ └── def add_x(number): │ ├── number = 0 │ └── return 3 + number │ return 3 + 0 │ return 3 ├── input_2 = 3 └── assert input_1 == input_2 assert 3 == 3I remove the commented lines from test_why_use_a_function
49def test_why_use_a_function(): 50 def add_x(number): 51 return 3 + number 52 53 assert_equal(add_x(0), 3) 54 assert_equal(add_x(1), 4) 55 assert_equal(add_x(2), 5) 56 assert_equal(add_x(3), 6) 57 assert_equal(add_x(4), 7) 58 assert_equal(add_x(5), 8) 59 assert_equal(add_x(6), 9) 60 assert_equal(add_x(7), 10) 61 assert_equal(add_x(8), 11) 62 assert_equal(add_x(9), 12) 63 64 65def test_positional_arguments():I use the assert_equal function for the assertions in test_identity_function
41def test_identity_function(): 42 def identity(the_input): 43 return the_input 44 45 # assert identity(None) == None 46 assert_equal(identity(None), object) 47 # assert identity(object) == object 48 assert_equal(identity(object), None) 49 50 51def test_why_use_a_function():the terminal is my friend, and shows AssertionError
AssertionError: assert None == <class 'object'>I change the expectation of the first assertion in test_identity_function
41def test_identity_function(): 42 def identity(the_input): 43 return the_input 44 45 # assert identity(None) == None 46 # assert_equal(identity(None), object) 47 assert_equal(identity(None), None) 48 # assert identity(object) == object 49 assert_equal(identity(object), None) 50 51 52def test_why_use_a_function():the terminal is my friend, and shows AssertionError
AssertionError: assert <class 'object'> == NoneI change the expectation of the second assertion in test_identity_function
41def test_identity_function(): 42 def identity(the_input): 43 return the_input 44 45 # assert identity(None) == None 46 # assert_equal(identity(None), object) 47 assert_equal(identity(None), None) 48 # assert identity(object) == object 49 # assert_equal(identity(object), None) 50 assert_equal(identity(object), object) 51 52 53def test_why_use_a_function():the test passes.
assert_equal(identity(object), object) -> None └── def assert_equal(input_1, input_2): ├── input_1 = identity(object) │ └── def identity(the_input): │ ├── the_input = object │ └── return the_input │ return object ├── input_2 = object └── assert input_1 == input_2 assert object == objectI remove the commented lines from test_identity_function
41def test_identity_function(): 42 def identity(the_input): 43 return the_input 44 45 assert_equal(identity(None), None) 46 assert_equal(identity(object), object) 47 48 49def test_why_use_a_function():I use the assert_equal function in test_constant_function
34def test_constant_function(): 35 def constant(): 36 return 'the same thing' 37 38 # assert constant() == 'the same thing' 39 assert_equal(constant(), 'not the same thing') 40 41 42def test_identity_function():the terminal is my friend, and shows AssertionError
AssertionError: assert 'the same thing' == 'not the same thing'I change the expectation to match reality in test_constant_function
34def test_constant_function(): 35 def constant(): 36 return 'the same thing' 37 38 # assert constant() == 'the same thing' 39 # assert_equal(constant(), 'not the same thing') 40 assert_equal(constant(), 'the same thing') 41 42 43def test_identity_function():the test passes.
assert_equal(constant(), 'the same thing') -> None └── def assert_equal(input_1, input_2): ├── input_1 = constant() │ └── def constant(): │ └── return 'the same thing' ├── input_2 = 'the same thing' └── assert input_1 == input_2 assert 'the same thing' == 'the same thing'I remove the commented lines from test_identity_function
34def test_constant_function(): 35 def constant(): 36 return 'the same thing' 37 38 assert_equal(constant(), 'the same thing') 39 40 41def test_identity_function():I add a git commit message
git commit --all --message \ 'extract assert_equal function'the terminal shows a summary of the changes then goes back to the command line.
extract assert_is_none function
The assertions in test_what_happens_after_functions_return, test_making_a_function_w_return_none, test_making_a_function_w_return and test_making_a_function_w_pass are the same, they check if the result of a function call is the same object as None.
assert function() is None
I can use a function to assert if something is None.
RED: make it fail
I go back to the terminal where the tests are running
I add a function named
assert_is_nonethat takes one input and asserts if the input is None1def assert_equal(input_1, input_2): 2 assert input_1 == input_2 3 4 5def assert_is_none(something): 6 assert something is None 7 8 9def test_making_a_function_w_pass():I use the new function for the assertion in test_making_a_function_w_pass
9def test_making_a_function_w_pass(): 10 def w_pass(): 11 pass 12 13 # assert w_pass() is None 14 assert_is_none(w_pass) 15 16 17def test_making_a_function_w_return():the terminal is my friend, and shows AssertionError
assert <function test_making_a_function_w_pass .<locals>.w_pass at 0xffffa76b5432> is None
GREEN: make it pass
I call the w_pass function in the assertion
9 def test_making_a_function_w_pass():
10 def w_pass():
11 pass
12
13 # assert w_pass() is None
14 # assert_is_none(w_pass)
15 assert_is_none(w_pass())
16
17
18 def test_making_a_function_w_return():
the test passes.
assert_is_none(w_pass()) -> None
└── def assert_is_none(something):
├── something = w_pass()
│ └── def w_pass():
│ └── pass
│ return None
└── assert something is None
assert None is None
REFACTOR: make it better
I remove the commented lines from test_making_a_function_w_pass
9def test_making_a_function_w_pass(): 10 def w_pass(): 11 pass 12 13 assert_is_none(w_pass()) 14 15 16def test_making_a_function_w_return():I use the assert_is_none function for the assertion in test_making_a_function_w_return
16def test_making_a_function_w_return(): 17 def w_return(): 18 return 19 20 # assert w_return() is None 21 assert_is_none(w_return) 22 23 24def test_making_a_function_w_return_none():the terminal is my friend, and shows AssertionError
assert <function test_making_a_function_w_return .<locals>.w_return at 0xffff7e654321> is NoneI call the w_return function in the assertion
16def test_making_a_function_w_return(): 17 def w_return(): 18 return 19 20 # assert w_return() is None 21 # assert_is_none(w_return) 22 assert_is_none(w_return()) 23 24 25def test_making_a_function_w_return_none():the test passes.
assert_is_none(w_return()) -> None └── def assert_is_none(something): ├── something = w_return() │ └── def w_return(): │ └── return │ return None └── assert something is None assert None is NoneI remove the commented lines from test_making_a_function_w_return
16def test_making_a_function_w_return(): 17 def w_return(): 18 return 19 20 assert_is_none(w_return()) 21 22 23def test_making_a_function_w_return_none():I use the assert_is_none function in test_making_a_function_w_return_none
23def test_making_a_function_w_return_none(): 24 def w_return_none(): 25 return None 26 27 # assert w_return_none() is None 28 assert_is_none(w_return_none) 29 30 31def test_what_happens_after_functions_return():the terminal is my friend, and shows AssertionError
assert <function test_making_a_function_w_return_none .<locals>.w_return_none at 0xffffa1234567> is NoneI call the w_return_none function in the assertion
23def test_making_a_function_w_return_none(): 24 def w_return_none(): 25 return None 26 27 # assert w_return_none() is None 28 # assert_is_none(w_return_none) 29 assert_is_none(w_return_none()) 30 31 32def test_what_happens_after_functions_return():the test passes.
assert_is_none(w_return_none()) -> None └── def assert_is_none(something): ├── something = w_return_none() │ └── def w_return_none(): │ └── return None └── assert something is None assert None is NoneI remove the commented lines from test_making_a_function_w_return_none
23def test_making_a_function_w_return_none(): 24 def w_return_none(): 25 return None 26 27 assert_is_none(w_return_none()) 28 29 30def test_what_happens_after_functions_return():I use the assert_is_none function in test_what_happens_after_functions_return
30def test_what_happens_after_functions_return(): 31 def return_leaves_the_function(): 32 return None 33 return 'only one way for this line to run' 34 35 # assert return_leaves_the_function() is None 36 assert_is_none(return_leaves_the_function) 37 38 39def test_constant_function():the terminal is my friend, and shows AssertionError
assert <function test_what_happens_after_functions_return .<locals>.return_leaves_the_function at 0xffffa01b2345> is NoneI call the return_leaves_the_function function in the assertion
30def test_what_happens_after_functions_return(): 31 def return_leaves_the_function(): 32 return None 33 return 'only one way for this line to run' 34 35 # assert return_leaves_the_function() is None 36 # assert_is_none(return_leaves_the_function) 37 assert_is_none(return_leaves_the_function()) 38 39 40def test_constant_function():the test passes.
assert_is_none(return_leaves_the_function()) -> None └── def assert_is_none(something): ├── something = return_leaves_the_function() │ └── def return_leaves_the_function(): │ └── return None └── assert something is None assert None is NoneI remove the commented lines from test_what_happens_after_functions_return
30def test_what_happens_after_functions_return(): 31 def return_leaves_the_function(): 32 return None 33 return 'only one way for this line to run' 34 35 assert_is_none(return_leaves_the_function()) 36 37 38def test_constant_function():I use the assert_is_none function in test_identity_function for the assertion that has None as its expectation
45def test_identity_function(): 46 def identity(the_input): 47 return the_input 48 49 # assert_equal(identity(None), None) 50 assert_is_none(identity(object)) 51 assert_equal(identity(object), object) 52 53 54def test_why_use_a_function():the terminal is my friend, and shows AssertionError
AssertionError: assert <class 'object'> is NoneI change the input in call to the identity function from object to None
45def test_identity_function(): 46 def identity(the_input): 47 return the_input 48 49 # assert_equal(identity(None), None) 50 # assert_is_none(identity(object)) 51 assert_is_none(identity(None)) 52 assert_equal(identity(object), object) 53 54 55def test_why_use_a_function():the test passes.
assert_is_none(identity(None)) -> None └── def assert_is_none(something): ├── something = identity(None) │ └── def identity(the_input): │ ├── the_input = None │ └── return the_input └── assert something is None assert None is NoneI remove the commented lines from test_identity_function
45def test_identity_function(): 46 def identity(the_input): 47 return the_input 48 49 assert_is_none(identity(None)) 50 assert_equal(identity(object), object) 51 52 53def test_why_use_a_function():I add a git commit message
git commit --all --message \ 'extract assert_is_none function'the terminal shows a summary of the changes then goes back to the command line.
test_keyword_arguments
test_positional_arguments shows that positional arguments must always be given in the right order which is a problem if I forget the order, especially if there are many inputs.
Another way to call a function is to use Keyword Arguments to make sure the function always gets the values for the inputs it expects without worrying about the order.
RED: make it fail
I go back to the terminal where the tests are running
I add a test for keyword arguments to
test_functions.py88 a_tuple = (0, 1, 2, 'n') 89 a_list = [0, 1, 2, 'n'] 90 assert_equal( 91 positional_arguments(a_tuple, a_list), 92 (a_tuple, a_list) 93 ) 94 95 96def test_keyword_arguments(): 97 assert_equal(keyword_arguments(), None) 98 99 100# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'keyword_arguments' is not definedbecause there is no definition for
keyword_argumentsin this file.
GREEN: make it pass
I add a function definition
96 def test_keyword_arguments():
97 def keyword_arguments():
98 return None
99
100 assert_equal(keyword_arguments(), None)
101
102
103 # Exceptions seen
the test passes.
keyword_arguments() -> None
└── def keyword_arguments():
└── return None
what is a keyword argument?
A keyword argument is a key-value pair that is used to pass input in a function call. Where key is a name, and the value is any object the function accepts.
REFACTOR: make it better
I add input to the function call with a name
96def test_keyword_arguments(): 97 def keyword_arguments(): 98 return None 99 100 # assert_equal(keyword_arguments(), None) 101 assert_equal( 102 keyword_arguments(first_input='first'), None 103 ) 104 105 106# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_keyword_arguments.<locals>.keyword_arguments() got an unexpected keyword argument 'first_input'because
the call to
keyword_argumentswhich belongs to test_keyword_arguments uses a name (first_input) and a value for the name ('first').the function definition (signature) of
keyword_argumentsdoes not allow any inputs when it is called since the parentheses are empty.the call to a function must match its signature (definition).
I add a name in parentheses to make the function take input
96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 def keyword_arguments(the_input): 99 return None 100 101 # assert_equal(keyword_arguments(), None) 102 assert_equal( 103 keyword_arguments(first_input='first'), None 104 ) 105 106 107# Exceptions seenthe terminal still shows TypeError because the names in the function call and function definition are different.
I change the name of the input in the function definition (
the_input) to match the name used in the function call (first_input)96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 def keyword_arguments(first_input): 100 return None 101 102 # assert_equal(keyword_arguments(), None) 103 assert_equal( 104 keyword_arguments(first_input='first'), None 105 ) 106 107 108# Exceptions seenthe test passes because the keyword I used to call the function matches the name in the function definition.
keyword_arguments(first_input='first') -> None └── def keyword_arguments(first_input): └── return NoneI add another keyword argument to the function call in test_keyword_arguments
96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 def keyword_arguments(first_input): 100 return None 101 102 # assert_equal(keyword_arguments(), None) 103 assert_equal( 104 # keyword_arguments(first_input='first'), None 105 keyword_arguments( 106 first_input='first', last_input='last', 107 ), 108 None 109 ) 110 111 112# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_keyword_arguments.<locals>.keyword_arguments() got an unexpected keyword argument 'last_input'. Did you mean 'first_input'?because
the call to
keyword_argumentswhich belongs to test_keyword_arguments uses two names (first_inputandlast_input) and values for the names ('first'and'last').the function definition (signature) of
keyword_argumentsonly allows one input when it is called.the call to a function must match its signature (definition).
I make the function take two inputs by adding another name in parentheses
96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 # def keyword_arguments(first_input): 100 def keyword_arguments(first_input, second_input): 101 return None 102 103 # assert_equal(keyword_arguments(), None) 104 assert_equal( 105 # keyword_arguments(first_input='first'), None 106 keyword_arguments( 107 first_input='first', last_input='last', 108 ), 109 None 110 ) 111 112 113# Exceptions seenthe terminal still shows TypeError because the names in the function call and function definition are different.
I change the name of the input in the function definition (
second_input) to match the name used in the function call (last_input)96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 # def keyword_arguments(first_input): 100 # def keyword_arguments(first_input, second_input): 101 def keyword_arguments(first_input, last_input): 102 return None 103 104 # assert_equal(keyword_arguments(), None) 105 assert_equal( 106 # keyword_arguments(first_input='first'), None 107 keyword_arguments( 108 first_input='first', last_input='last', 109 ), 110 None 111 ) 112 113 114# Exceptions seenthe test passes because the keywords I used to call the function match the names in the function definition.
keyword_arguments( first_input='first', last_input='last' ) -> None └── def keyword_arguments(first_input, last_input): └── return NoneI change the expectation of the assertion
96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 # def keyword_arguments(first_input): 100 # def keyword_arguments(first_input, second_input): 101 def keyword_arguments(first_input, last_input): 102 return None 103 104 # assert_equal(keyword_arguments(), None) 105 assert_equal( 106 # keyword_arguments(first_input='first'), None 107 keyword_arguments( 108 first_input='first', last_input='last', 109 ), 110 # None 111 ('first', 'last') 112 ) 113 114 115# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert None == ('first', 'last')because when I call
keyword_argumentswithfirst_input='first'andlast_input='last'as inputs, it returns None. Using substitution since I can treat a call to a function as the object it returns.assert_equal( keyword_arguments( first_input='first', last_input='last', ), ('first', 'last') ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = keyword_arguments( │ first_input='first', last_input='last', │ ) │ └── def keyword_arguments( │ first_input, last_input │ ): │ └── return None ├── input_2 = ('first', 'last') └── assert input_1 == input_2 assert None == ('first', 'last')which raises AssertionError since None is NOT equal to a tuple.
I change the return statement to make the function return its inputs as output (like the identity function)
96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 # def keyword_arguments(first_input): 100 # def keyword_arguments(first_input, second_input): 101 def keyword_arguments(first_input, last_input): 102 # return None 103 return first_input, last_input 104 105 # assert_equal(keyword_arguments(), None) 106 assert_equal( 107 # keyword_arguments(first_input='first'), None 108 keyword_arguments( 109 first_input='first', last_input='last', 110 ), 111 # None 112 ('first', 'last') 113 ) 114 115 116# Exceptions seenthe test passes, because the function always returns
first_input, last_inputand the call in the test sendsfirst_input='first'andlast_input='last'keyword_arguments( first_input='first', last_input='last' ) -> ('first', 'last') └── def keyword_arguments(first_input, last_input): ├── first_input = 'first' ├── last_input = 'last' └── return first_input, last_input return 'first' , 'last'The bad thing about giving arguments this way, is I must use the exact names in the function definition when I make a call to the function. The good thing about giving arguments this way is that the names do not have to match the order in the function definition. I add an assertion with the keyword arguments given out of order
105 # assert_equal(keyword_arguments(), None) 106 assert_equal( 107 # keyword_arguments(first_input='first'), None 108 keyword_arguments( 109 first_input='first', last_input='last', 110 ), 111 # None 112 ('first', 'last') 113 ) 114 assert_equal( 115 keyword_arguments( 116 last_input='last', first_input='first', 117 ), 118 ('last', 'first') 119 ) 120 121 122# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('first', 'last') == ('last', 'first')because the function always returns
first_input, last_inputand this test calls the function with'last'aslast_inputand'first'asfirst_input, the order does not matter because I used the names.assert_equal( keyword_arguments( last_input='last', first_input='first', ), ('last', 'first') ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = keyword_arguments( │ last_input='last', first_input='first', │ ) │ └── def keyword_arguments( │ first_input, last_input │ ): │ ├── first_input = 'first' │ ├── last_input = 'last' │ └── return first_input, last_input │ return 'first' , 'last' ├── input_2 = ('last', 'first') └── assert input_1 == input_2 assert ('first', 'last') == ('last', 'first')I change my expectation to match reality
105 # assert_equal(keyword_arguments(), None) 106 assert_equal( 107 # keyword_arguments(first_input='first'), None 108 keyword_arguments( 109 first_input='first', last_input='last', 110 ), 111 # None 112 ('first', 'last') 113 ) 114 assert_equal( 115 keyword_arguments( 116 last_input='last', first_input='first', 117 ), 118 # ('last', 'first') 119 ('first', 'last') 120 ) 121 122 123# Exceptions seenthe test passes.
keyword_arguments( last_input='last', first_input='first' ) -> ('first', 'last') └── def keyword_arguments(first_input, last_input): ├── first_input = 'first' ├── last_input = 'last' └── return first_input, last_input return 'first' , 'last'I add variables for
'first'and'last'in test_keyword_arguments96def test_keyword_arguments(): 97 # def keyword_arguments(): 98 # def keyword_arguments(the_input): 99 # def keyword_arguments(first_input): 100 # def keyword_arguments(first_input, second_input): 101 def keyword_arguments(first_input, last_input): 102 # return None 103 return first_input, last_input 104 105 first, last = 'first', 'last' 106 107 # assert_equal(keyword_arguments(), None)I use the variables to remove repetition of
'first'and'last'from test_keyword_arguments107 # assert_equal(keyword_arguments(), None) 108 assert_equal( 109 # keyword_arguments(first_input='first'), None 110 keyword_arguments( 111 # first_input='first', last_input='last', 112 first_input=first, last_input=last, 113 ), 114 # None 115 # ('first', 'last') 116 (first, last) 117 ) 118 assert_equal( 119 keyword_arguments( 120 # last_input='last', first_input='first', 121 last_input=last, first_input=first, 122 ), 123 # ('last', 'first') 124 # ('first', 'last') 125 (first, last) 126 ) 127 128 129# Exceptions seenthe test is still green.
I add another assertion to test_keyword_arguments
118 assert_equal( 119 keyword_arguments( 120 # last_input='last', first_input='first', 121 last_input=last, first_input=first, 122 ), 123 # ('last', 'first') 124 # ('first', 'last') 125 (first, last) 126 ) 127 128 assert_equal( 129 keyword_arguments( 130 last_input=0, first_input=1, 131 ), 132 (0, 1) 133 ) 134 135 136# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1, 0) == (0, 1)because the function always returns
first_input, last_inputand this test calls the function with0aslast_inputand1asfirst_input, the order does not matter because I used the names.assert_equal( keyword_arguments( last_input=0, first_input=1, ), (0, 1) ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = keyword_arguments( │ last_input=0, first_input=1, │ ) │ └── def keyword_arguments( │ first_input, last_input │ ): │ ├── first_input = 1 │ ├── last_input = 0 │ └── return first_input, last_input │ return 1 , 0 ├── input_2 = (0, 1) └── assert input_1 == input_2 assert (1, 0) == (0, 1)I change my expectation to match reality
118 assert_equal( 119 keyword_arguments( 120 # last_input='last', first_input='first', 121 last_input=last, first_input=first, 122 ), 123 # ('last', 'first') 124 # ('first', 'last') 125 (first, last) 126 ) 127 128 assert_equal( 129 keyword_arguments( 130 last_input=0, first_input=1, 131 ), 132 # (0, 1) 133 (1, 0) 134 ) 135 136 137# Exceptions seenthe test passes.
keyword_arguments( last_input=0, first_input=1 ) -> (1, 0) def keyword_arguments(first_input, last_input): ├── first_input = 1 ├── last_input = 0 └── return first_input, last_input return 1 , 0I add an assertion to test_keyword_arguments with a tuple and a list
128 assert_equal( 129 keyword_arguments( 130 last_input=0, first_input=1, 131 ), 132 # (0, 1) 133 (1, 0) 134 ) 135 136 a_tuple = (0, 1, 2, 'n') 137 a_list = [0, 1, 2, 'n'] 138 assert_equal( 139 keyword_arguments( 140 first_input=a_list, 141 last_input=a_tuple, 142 ), 143 (a_tuple, a_list) 144 ) 145 146 147# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ([1, 2, 3, 'n...0, 1, 2, 'n')) == ((1, 2, 3, 'n...0, 1, 2, 'n'])because the function always returns
first_input, last_inputand this test calls the function with[0, 1, 2, 'n']asfirst_inputand(0, 1, 2, 'n')aslast_input, the order does not matter because I used the names├── a_tuple = (0, 1, 2, 'n') ├── a_list = [0, 1, 2, 'n'] └── assert_equal( keyword_arguments( first_input=a_list, last_input=a_tuple, ), (a_tuple, a_list) ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = keyword_arguments( │ first_input=a_list, │ last_input=a_tuple, │ ) │ └── def keyword_arguments( │ first_input, last_input │ ): │ ├── first_input = a_list │ ├── last_input = a_tuple │ └── return first_input, last_input │ return a_list , a_tuple ├── input_2 = (a_tuple, a_list) └── assert input_1 == input_2 assert (a_list, a_tuple) == (a_tuple, a_list)I change reality to match my expectation
128 assert_equal( 129 keyword_arguments( 130 last_input=0, first_input=1, 131 ), 132 # (0, 1) 133 (1, 0) 134 ) 135 136 a_tuple = (0, 1, 2, 'n') 137 a_list = [0, 1, 2, 'n'] 138 assert_equal( 139 keyword_arguments( 140 # first_input=a_list, 141 # last_input=a_tuple, 142 first_input=a_tuple, 143 last_input=a_list, 144 ), 145 (a_tuple, a_list) 146 ) 147 148 149# Exceptions seenthe test passes.
├── a_tuple = (0, 1, 2, 'n') ├── a_list = [0, 1, 2, 'n'] └── keyword_arguments( first_input=a_list, last_input=a_tuple, ) -> (a_tuple, a_list) └── def keyword_arguments(first_input, last_input): ├── first_input = a_list ├── last_input = a_tuple └── return first_input, last_input return a_tuple , a_listkeyword_argumentsandpositional_argumentsare the same function intest_functions.py, they alwaysreturn first_input, last_inputTheir names are different
def positional_arguments(first_input, last_input): def keyword_arguments(first_input, last_input):The difference that matters in the tests is how I call them
I have to give the input in order when I use positional arguments because I do NOT use the names from the function definition when I call it
positional_arguments('first', 'last') -> return ('first', 'last')positional_arguments('last', 'first') -> return ('last', 'first')positional_arguments(0, 1) -> return (0, 1)positional_arguments((0, 1, 2, 'n'), [0, 1, 2, 'n']) -> return ((0, 1, 2, 'n'), [0, 1, 2, 'n'])keyword_arguments('last', 'first') -> return ('last', 'first')I can give the input in any order when I use keyword arguments because I use the names from the function definition when I call it
keyword_arguments( first_input='first', last_input='last', ) -> return ('first', 'last')keyword_arguments( last_input='last', first_input='first', ) -> return ('first', 'last')keyword_arguments(last_input=0, first_input=1) -> return (1, 0)keyword_arguments( first_input=(0, 1, 2, 'n'), last_input=[0, 1, 2, 'n'], ) -> return ((0, 1, 2, 'n'), [0, 1, 2, 'n'])
I call the positional_arguments function with keyword arguments to show that the two functions are the same
136 a_tuple = (0, 1, 2, 'n') 137 a_list = [0, 1, 2, 'n'] 138 assert_equal( 139 keyword_arguments( 140 # first_input=a_list, 141 # last_input=a_tuple, 142 first_input=a_tuple, 143 last_input=a_list, 144 ), 145 (a_tuple, a_list) 146 ) 147 148 a_set = {0, 1, 2, 'n'} 149 a_dictionary = {'key': 'value'} 150 assert_equal( 151 positional_arguments( 152 last_input=a_dictionary, 153 first_input=a_set, 154 ), 155 (a_set, a_dictionary) 156 ) 157 158 159# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'positional_arguments' is not definedbecause the positional_arguments function belongs to the test_positional_arguments function and I cannot reach it from outside test_positional_arguments.
I move the positional_arguments function out of test_positional_arguments so that it can be called from anywhere in the file
66 assert_equal(add_x(9), 12) 67 68 69def positional_arguments(first_input, last_input): 70 return first_input, last_input 71 72 73def test_positional_arguments(): 74 first, last = 'first', 'last' 75 76 assert_equal( 77 positional_arguments(first, last), 78 (first, last) 79 ) 80 assert_equal( 81 positional_arguments(last, first), 82 (last, first) 83 ) 84 85 assert_equal( 86 positional_arguments(0, 1), (0, 1) 87 ) 88 89 a_tuple = (0, 1, 2, 'n')the test passes because these two calls are the same
positional_arguments( last_input=a_dictionary, first_input=a_set, ) positional_arguments( a_set, a_dictionary, )When
positional_arguments(last_input=a_dictionary, first_input=a_set)runspositional_arguments( last_input=a_dictionary, first_input=a_set, ) -> (a_set, a_dictionary) └── def positional_arguments(first_input, last_input): ├── first_input = a_set ├── last_input = a_dictionary └── return first_input, last_input return a_set , a_dictionaryWhen
positional_arguments(a_set, a_dictionary)runspositional_arguments( a_set, a_dictionary ) -> (a_set, a_dictionary) └── def positional_arguments(first_input, last_input): ├── first_input = a_set ├── last_input = a_dictionary └── return first_input, last_input return a_set , a_dictionary
I add an assertion to test_positional_arguments to show that I can call the keyword_arguments function with positional arguments
89 a_tuple = (0, 1, 2, 'n') 90 a_list = [0, 1, 2, 'n'] 91 assert_equal( 92 positional_arguments(a_tuple, a_list), 93 (a_tuple, a_list) 94 ) 95 96 a_set = {0, 1, 2, 'n'} 97 a_dictionary = {'key': 'value'} 98 assert_equal( 99 keyword_arguments( 100 a_set, a_dictionary, 101 ), 102 (a_set, a_dictionary) 103 ) 104 105 106def test_keyword_arguments():the terminal is my friend, and shows NameError
NameError: name 'keyword_arguments' is not definedbecause the keyword_arguments function belongs to the test_keyword_arguments function and I cannot reach it from outside test_keyword_arguments, yet.
I move the keyword_arguments function out of test_keyword_arguments so that it can be called from anywhere in the file
96 a_set = {0, 1, 2, 'n'} 97 a_dictionary = {'key': 'value'} 98 assert_equal( 99 keyword_arguments( 100 a_set, a_dictionary, 101 ), 102 (a_set, a_dictionary) 103 ) 104 105 106def keyword_arguments(first_input, last_input): 107 return first_input, last_input 108 109 110def test_keyword_arguments(): 111 # def keyword_arguments(): 112 # def keyword_arguments(the_input): 113 # def keyword_arguments(first_input): 114 # def keyword_arguments(first_input, second_input): 115 # return Nonethe test passes because these two calls are the same
keyword_arguments( a_set, a_dictionary, ) keyword_arguments( last_input=a_dictionary, first_input=a_set, )When
keyword_arguments(a_set, a_dictionary)runskeyword_arguments( a_set, a_dictionary ) -> (a_set, a_dictionary) └── def keyword_arguments(first_input, last_input): ├── first_input = a_set ├── last_input = a_dictionary └── return first_input, last_input return a_set , a_dictionaryWhen
keyword_arguments(last_input=a_dictionary, first_input=a_set)runskeyword_arguments( last_input=a_dictionary, first_input=a_set, ) -> (a_set, a_dictionary) └── def keyword_arguments(first_input, last_input): ├── first_input = a_set ├── last_input = a_dictionary └── return first_input, last_input return a_set , a_dictionary
I remove the commented lines from test_keyword_arguments
110def test_keyword_arguments(): 111 first, last = 'first', 'last' 112 113 assert_equal( 114 keyword_arguments( 115 first_input=first, last_input=last, 116 ), 117 (first, last) 118 ) 119 assert_equal( 120 keyword_arguments( 121 last_input=last, first_input=first, 122 ), 123 (first, last) 124 )126 assert_equal( 127 keyword_arguments( 128 last_input=0, first_input=1, 129 ), 130 (1, 0) 131 )133 a_tuple = (0, 1, 2, 'n') 134 a_list = [0, 1, 2, 'n'] 135 assert_equal( 136 keyword_arguments( 137 first_input=a_tuple, 138 last_input=a_list, 139 ), 140 (a_tuple, a_list) 141 )143 a_set = {0, 1, 2, 'n'} 144 a_dictionary = {'key': 'value'} 145 assert_equal( 146 positional_arguments( 147 last_input=a_dictionary, 148 first_input=a_set, 149 ), 150 (a_set, a_dictionary) 151 ) 152 153 154# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_keyword_arguments'the terminal shows a summary of the changes then goes back to the command line.
test_args_and_kwargs
Can I call a function with both positional and keyword arguments?
RED: make it fail
I go back to the terminal where the tests are running
I add a test
143 a_set = {0, 1, 2, 'n'} 144 a_dictionary = {'key': 'value'} 145 assert_equal( 146 positional_arguments( 147 last_input=a_dictionary, 148 first_input=a_set, 149 ), 150 (a_set, a_dictionary) 151 ) 152 153 154def test_args_and_kwargs(): 155 assert_equal( 156 args_and_kwargs( 157 last_input='last', 'first', 158 ), 159 ('first', 'last') 160 ) 161 162 163# Exceptions seenthe terminal is my friend, and shows SyntaxError
SyntaxError: positional argument follows keyword argumentbecause I cannot put keyword arguments before positional arguments.
GREEN: make it pass
I add SyntaxError to the list of Exceptions seen, in
test_functions.py163# Exceptions seen 164# AssertionError 165# NameError 166# TypeError 167# SyntaxErrorI change the order of the arguments to follow Python rules
154def test_args_and_kwargs(): 155 assert_equal( 156 args_and_kwargs( 157 # last_input='last', 'first', 158 'first', last_input='last' 159 ), 160 ('first', 'last') 161 ) 162 163 164# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'args_and_kwargs' is not definedbecause I have not given a definition for the name yet.
I add a function definition for
args_and_kwargs154def test_args_and_kwargs(): 155 def args_and_kwargs(): 156 return None 157 158 assert_equal( 159 args_and_kwargs( 160 # last_input='last', 'first', 161 'first', last_input='last' 162 ), 163 ('first', 'last') 164 ) 165 166 167# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_args_and_kwargs.<locals>.args_and_kwargs() got an unexpected keyword argument 'last_input'because
the call to
args_and_kwargswhich belongs to test_args_and_kwargs uses a keyword argument (last_input='last').the function definition (signature) of
args_and_kwargsdoes not allow any inputs when it is called since the parentheses are empty.the call to a function must match its signature (definition).
I add
last_inputto the parentheses ofargs_and_kwargs154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 def args_and_kwargs(last_input): 157 return None 158 159 assert_equal( 160 args_and_kwargs( 161 # last_input='last', 'first', 162 'first', last_input='last' 163 ), 164 ('first', 'last') 165 ) 166 167 168# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_args_and_kwargs.<locals>.args_and_kwargs() got multiple values for argument 'last_input'because
the call to
args_and_kwargswhich belongs to test_args_and_kwargs uses both positional and keyword arguments for the same parameter ('first'andlast_input='last').the function definition (signature) of
args_and_kwargstakes one argument (last_input). How does Python know which value to use forlast_inputif I use the position and the name?args_and_kwargs_arguments('first', last_input='last',) └── def args_and_kwargs_arguments(last_input): ├── last_input = 'first' ? └── last_input = 'last' ?the call to a function must match its signature (definition).
I add
first_inputto the parentheses ofargs_and_kwargsto make it clearer154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 # def args_and_kwargs(last_input): 157 def args_and_kwargs(last_input, first_input): 158 return None 159 160 assert_equal( 161 args_and_kwargs( 162 # last_input='last', 'first', 163 'first', last_input='last' 164 ), 165 ('first', 'last') 166 ) 167 168 169# Exceptions seenthe terminal still shows TypeError because I have not fixed the problem, the call has confusing values. Python cannot tell the difference between the two values because I gave a positional argument (
first), the function definition has thelast_inputparameter in the first position, and I gave a value with the namelast_input.args_and_kwargs_arguments('first', last_input='last',) └── def args_and_kwargs_arguments(last_input, first_input): ├── last_input = 'first' ? ├── last_input = 'last' ? └── first_input = ?The call gives the values for
last_inputas both'first'and'last', it would be like defining the function with the same name twice154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 # def args_and_kwargs(last_input): 157 # def args_and_kwargs(last_input, first_input): 158 def args_and_kwargs(last_input, last_input): 159 return Nonethe terminal is my friend, and shows SyntaxError
SyntaxError: duplicate argument 'last_input' in function definitionI use the right names and put them in the right order
154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 # def args_and_kwargs(last_input): 157 # def args_and_kwargs(last_input, first_input): 158 # def args_and_kwargs(last_input, last_input): 159 def args_and_kwargs(first_input, last_input): 160 return Nonethe terminal is my friend, and shows AssertionError
AssertionError: assert None == ('first', 'last')because when I call
args_and_kwargswith'first'andlast_input='last'as inputs, it returns None which raises AssertionError since None is NOT equal to a tuple.├── a_tuple = (0, 1, 2, 'n') ├── a_list = [0, 1, 2, 'n'] └── assert_equal( args_and_kwargs( 'first', last_input='last' ), ('first', 'last') ) -> None └── def assert_equal(input_1, input_2): ├── input_1 = args_and_kwargs( │ 'first', last_input='last' │ ) │ └── def args_and_kwargs( │ first_input, last_input │ ): │ └── return None ├── input_2 = ('first', 'last') └── assert input_1 == input_2 assert None == ('first', 'last')I change the return statement to give the test what it wants
154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 # def args_and_kwargs(last_input): 157 # def args_and_kwargs(last_input, first_input): 158 # def args_and_kwargs(last_input, last_input): 159 def args_and_kwargs(first_input, last_input): 160 # return None 161 return first_input, last_inputthe test passes.
args_and_kwargs( 'first', last_input='last', ) -> ('first', 'last') └── def args_and_kwargs(first_input, last_input): └── return first_input, last_input return 'first' , 'last'I add variables for
'first'and'last'in test_args_and_kwargs154def test_args_and_kwargs(): 155 # def args_and_kwargs(): 156 # def args_and_kwargs(last_input): 157 # def args_and_kwargs(last_input, first_input): 158 # def args_and_kwargs(last_input, last_input): 159 def args_and_kwargs(first_input, last_input): 160 # return None 161 return first_input, last_input 162 163 first, last = 'first', 'last' 164 165 assert_equal( 166 args_and_kwargs( 167 # last_input='last', 'first', 168 'first', last_input='last' 169 ), 170 ('first', 'last') 171 ) 172 173 174# Exceptions seenI use the new variables to remove repetition of
'first'and'last'from test_args_and_kwargs163 first, last = 'first', 'last' 164 165 assert_equal( 166 args_and_kwargs( 167 # last_input='last', 'first', 168 # 'first', last_input='last' 169 first, last_input=last 170 ), 171 # ('first', 'last') 172 (first, last) 173 ) 174 175 176# Exceptions seenthe test is still green.
I remove the commented lines from test_args_and_kwargs
154def test_args_and_kwargs(): 155 def args_and_kwargs(first_input, last_input): 156 return first_input, last_input 157 158 first, last = 'first', 'last' 159 160 assert_equal( 161 args_and_kwargs( 162 first, last_input=last 163 ), 164 (first, last) 165 ) 166 167 168# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_args_and_kwargs'the terminal shows a summary of the changes then goes back to the command line.
I can call a function with positional and keyword arguments.
test_optional_arguments
I can make an argument of a function optional, which means a value does not need to be given for it when the function is called.
RED: make it fail
I go back to the terminal where the tests are running
I add a test to
test_functions.py160 assert_equal( 161 args_and_kwargs( 162 first, last_input=last 163 ), 164 (first, last) 165 ) 166 167 168def test_optional_arguments(): 169 first_name, last_name = 'jane', 'doe' 170 assert_equal( 171 optional_arguments( 172 first_name, last_input=last_name, 173 ), 174 (first_name, last_name) 175 ) 176 177 178# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'optional_arguments' is not definedbecause …
GREEN: make it pass
I add the function definition for optional_arguments
168def test_optional_arguments():
169 def optional_arguments(first_input, last_input):
170 return first_input, last_input
171
172 first_name, last_name = 'jane', 'doe'
173 assert_equal(
174 optional_arguments(
175 first_name, last_input=last_name,
176 ),
177 (first_name, last_name)
178 )
179
180
181# Exceptions seen
the test passes.
REFACTOR: make it better
I remove
last_input=last_namefrom the call tooptional_argumentsto show that it is a required argument168def test_optional_arguments(): 169 def optional_arguments(first_input, last_input): 170 return first_input, last_input 171 172 first_name, last_name = 'jane', 'doe' 173 assert_equal( 174 optional_arguments( 175 # first_name, last_input=last_name, 176 first_name, 177 ), 178 (first_name, last_name) 179 ) 180 181 182# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_optional_arguments.<locals>.optional_arguments() missing 1 required positional argument: 'last_input'because the
last_inputargument MUST be given when this function is called (it is required).I give the argument a default value to make it optional
168def test_optional_arguments(): 169 # def optional_arguments(first_input, last_input): 170 def optional_arguments( 171 first_input, last_input='doe' 172 ): 173 return first_input, last_input 174 175 first_name, last_name = 'jane', 'doe' 176 assert_equal( 177 optional_arguments( 178 # first_name, last_input=last_name, 179 first_name, 180 ), 181 (first_name, last_name) 182 ) 183 184 185# Exceptions seenthe test passes because I do not need to give a value for the
last_inputparameter when I call the function since there is a default value for thelast_inputparameter of the function (doe).These two calls are the same
optional_arguments('jane') optional_arguments('jane', last_input='doe')When
optional_arguments('jane')runsoptional_arguments('jane') -> ('jane', 'doe') └── def optional_arguments(first_input, last_input='doe'): ├── first_input = 'jane' ├── last_input = 'doe' # use default value └── return first_input, last_input return 'jane' , 'doe'When
optional_arguments(first_input, last_input='doe')runsoptional_arguments( 'jane', last_input='doe' ) -> ('jane', 'doe') └── def optional_arguments(first_input, last_input='doe'): ├── first_input = 'jane' ├── last_input = 'doe' # use given value └── return first_input, last_input return 'jane' , 'doe'
A function uses the default value for a parameter when it is called without the parameter.
I add another assertion to show that I can still call the function with different values
175 first_name, last_name = 'jane', 'doe' 176 assert_equal( 177 optional_arguments( 178 # first_name, last_input=last_name, 179 first_name, 180 ), 181 (first_name, last_name) 182 ) 183 184 first_name, blow = 'joe', 'blow' 185 assert_equal( 186 optional_arguments( 187 first_name, blow 188 ), 189 () 190 ) 191 192 193# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('joe', 'blow') == ()I change my expectation to match reality
184 first_name, blow = 'joe', 'blow' 185 assert_equal( 186 optional_arguments( 187 first_name, blow 188 ), 189 # () 190 (first_name, blow) 191 ) 192 193 194# Exceptions seenthe test passes.
├── first_name = 'joe' ├── blow = 'blow' └── optional_arguments( first_name, blow ) -> ('joe', 'blow') └── def optional_arguments(first_input, last_input='doe'): ├── first_input = first_name ├── last_input = blow # use given value └── return first_input, last_input return 'joe' , 'blow'I add another assertion to test_optional_arguments
184 first_name, blow = 'joe', 'blow' 185 assert_equal( 186 optional_arguments( 187 first_name, blow 188 ), 189 # () 190 (first_name, blow) 191 ) 192 193 first_name = 'john' 194 assert_equal( 195 optional_arguments( 196 first_input=first_name, 197 ), 198 () 199 ) 200 201 202# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('john', 'doe') == ()I change my expectation to match reality
193 first_name = 'john' 194 assert_equal( 195 optional_arguments( 196 first_input=first_name, 197 ), 198 # () 199 (first_name, last_name) 200 ) 201 202 203# Exceptions seenthe test passes because I do not need to give a value for the
last_inputparameter in the call tooptional_argumentssince there is a default value for thelast_inputparameter of theoptional_argumentsfunction (doe). This means thatoptional_arguments('john')is the same as
optional_arguments('john', last_input='doe')├── first_name = 'john' └── optional_arguments( first_input=first_name ) -> ('john', 'doe') └── def optional_arguments(first_input, last_input='doe'): ├── first_input = first_name ├── last_input = 'doe' # use default value └── return first_input, last_input return 'john' , 'doe'A function uses the default value for a parameter when it is called without the parameter.
I add one more assertion to test_optional_arguments
193 first_name = 'john' 194 assert_equal( 195 optional_arguments( 196 first_input=first_name, 197 ), 198 # () 199 (first_name, last_name) 200 ) 201 202 last_name = 'smith' 203 assert_equal( 204 optional_arguments( 205 last_input=last_name, 206 first_input=first_name, 207 ), 208 (last_name, first_name) 209 ) 210 211 212# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('john', 'smith') == ('smith', 'john')I change my expectation to match reality
202 last_name = 'smith' 203 assert_equal( 204 optional_arguments( 205 last_input=last_name, 206 first_input=first_name, 207 ), 208 # (last_name, first_name) 209 (first_name, last_name) 210 ) 211 212 213# Exceptions seenthe test passes.
├── first_name = 'john' ├── last_name = 'smith' └── optional_arguments( last_input=last_name, first_input=first_name, ) -> ('john', 'smith') └── def optional_arguments(first_input, last_input='doe'): ├── first_input = first_name ├── last_input = smith # use given value └── return first_input, last_input return 'john' , 'smith'I remove the commented lines from test_optional_arguments
168def test_optional_arguments(): 169 def optional_arguments( 170 first_input, last_input='doe' 171 ): 172 return first_input, last_input 173 174 first_name, last_name = 'jane', 'doe' 175 assert_equal( 176 optional_arguments( 177 first_name, 178 ), 179 (first_name, last_name) 180 )174 first_name, blow = 'joe', 'blow' 175 assert_equal( 176 optional_arguments( 177 first_name, blow 178 ), 179 (first_name, blow) 180 )190 first_name = 'john' 191 assert_equal( 192 optional_arguments( 193 first_input=first_name, 194 ), 195 (first_name, last_name) 196 )198 last_name = 'smith' 199 assert_equal( 200 optional_arguments( 201 last_input=last_name, 202 first_input=first_name, 203 ), 204 (first_name, last_name) 205 ) 206 207 208# Exceptions seenI add a git commit message in the other terminal
git commit --all --message \ 'add test_optional_arguments'the terminal shows a summary of the changes then goes back to the command line.
I can make a function with optional and required arguments.
These four functions - keyword_arguments, positional_arguments, args_and_kwargs and optional_arguments are the same, they always return first_input, last_input, their names are different.
def positional_arguments(first_input, last_input):
def keyword_arguments(first_input, last_input):
def args_and_kwargs(first_input, last_input):
def optional_arguments(first_input, last_input='doe'):
first_input and last_input are also names (variables), they can be any names. The difference that matters in the tests is how I call the functions
positional_arguments('first', 'last')
-> return 'first', 'last'
positional_arguments('last', 'first')
-> return 'last', 'first'
positional_arguments(
first_input=[0, 1, 2, 'n'],
last_input=(0, 1, 2, 'n')
) -> return [0, 1, 2, 'n'], (0, 1, 2, 'n')
keyword_arguments(
first_input='first', last_input='last'
) -> return 'first', 'last'
keyword_arguments(
last_input='last', first_input='first'
) -> return 'first', 'last'
keyword_arguments('last', 'first')
-> return 'last', 'first'
args_and_kwargs('first', last_input='last')
-> return 'first', 'last'
optional_arguments('jane', last_input='doe')
-> return 'jane', 'doe'
optional_arguments('jane')
-> return 'jane', 'doe'
optional_arguments('joe', 'blow')
-> return 'joe', 'blow'
optional_arguments(
first_input='john', last_input='smith'
) -> return 'john', 'smith'
Tip
As a rule of thumb I use keyword arguments when the function takes two or more inputs so I do not have to remember the order.
test_unknown_number_of_arguments
I can make functions that take any number of positional and keyword arguments. This means I do not need to know how many inputs the function should take when it is called, it can handle whatever I give it.
RED: make it fail
I go back to the terminal where the tests are running
I add test_unknown_number_of_arguments to
test_functions.py198 last_name = 'smith' 199 assert_equal( 200 optional_arguments( 201 last_input=last_name, 202 first_input=first_name, 203 ), 204 (first_name, last_name) 205 ) 206 207 208def test_unknown_number_of_arguments(): 209 assert_equal( 210 unknown_number_of_arguments( 211 0, 1, a=2, b=3, 212 ), 213 None 214 ) 215 216 217# Exceptions seenthe terminal is my friend, and shows NameError
NameError: name 'unknown_number_of_arguments' is not definedbecause
test_functions.pydoes not haveunknown_number_of_arguments.
GREEN: make it pass
I add the function
208def test_unknown_number_of_arguments(): 209 def unknown_number_of_arguments(): 210 return None 211 212 assert_equal( 213 unknown_number_of_arguments( 214 0, 1, a=2, b=3, 215 ), 216 None 217 ) 218 219 220# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() got an unexpected keyword argument 'a'because the assertion called
unknown_number_of_argumentswith a keyword argument namedaand the function definition does not allow any inputs, the parentheses are empty.I add
ato the function definition208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 def unknown_number_of_arguments(a): 211 return Nonethe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() got multiple values for argument 'a'I had this same problem in test_args_and_kwargs. Python cannot tell if
ais a positional or keyword argument based on my function definition. It cannot tell if0or2is the value fora.unknown_number_of_arguments(0, 1, a=2, b=3,) └── def unknown_number_of_arguments(a): ├── a = 0 ? └── a = 2 ?
double starred expressions
Python has a way for a function to take any number of keyword arguments without knowing how many they are. It is the double starred expression (**).
I use a double starred expression to replace
ain the parentheses208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 def unknown_number_of_arguments(**kwargs): 212 return Nonethe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() takes 0 positional arguments but 2 were givenI add
xas the name of the first positional argument208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 def unknown_number_of_arguments(**kwargs, x): 213 return Nonethe terminal is my friend, and shows SyntaxError
SyntaxError: arguments cannot follow var-keyword argumenta reminder that I cannot put positional arguments after keyword arguments.
I change the order of the inputs in
unknown_number_of_arguments208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 def unknown_number_of_arguments(x, **kwargs): 214 return Nonethe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() takes 1 positional argument but 2 were givenI add
yas the name of the other positional argument208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 def unknown_number_of_arguments(x, y, **kwargs): 215 return None 216 217 assert_equal( 218 unknown_number_of_arguments( 219 0, 1, a=2, b=3, 220 ), 221 None 222 ) 223 224 225# Exceptions seenthe test passes.
unknown_number_of_arguments(0, 1, a=2, b=3,) -> None └── def unknown_number_of_arguments(x, y, **kwargs): └── return None
REFACTOR: make it better
I add an assertion to see what happens if I call the function with three keyword arguments
217 assert_equal( 218 unknown_number_of_arguments( 219 0, 1, a=2, b=3, 220 ), 221 None 222 ) 223 224 assert_equal( 225 unknown_number_of_arguments( 226 0, 1, a=2, b=3, c=4, 227 ), 228 () 229 ) 230 231 232# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert None == ()I change my expectation to match reality
224 assert_equal( 225 unknown_number_of_arguments( 226 0, 1, a=2, b=3, c=4, 227 ), 228 # () 229 None 230 ) 231 232 233# Exceptions seenthe test passes because the function can take any number of keyword arguments without knowing how many are in the call.
unknown_number_of_arguments(0, 1, a=2, b=3, c=4,) -> None └── def unknown_number_of_arguments(x, y, **kwargs): └── return NoneI add an assertion to see what happens when I call the function with three positional arguments
224 assert_equal( 225 unknown_number_of_arguments( 226 0, 1, a=2, b=3, c=4, 227 ), 228 # () 229 None 230 ) 231 232 assert_equal( 233 unknown_number_of_arguments( 234 0, 1, 2, a=3, b=4, c=5, 235 ), 236 None 237 ) 238 239 240# Exceptions seenthe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() takes 2 positional arguments but 3 were giventhe function definition only allows two positional arguments not three.
I change the definition of the
unknown_number_of_argumentsfunction to make it take three positional arguments208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 # def unknown_number_of_arguments(x, y, **kwargs): 215 def unknown_number_of_arguments(x, y, z, **kwargs): 216 return Nonethe terminal is my friend, and shows TypeError
TypeError: test_unknown_number_of_arguments .<locals>.unknown_number_of_arguments() missing 1 required positional argument: 'z'because the previous call to the function uses two positional arguments and the function now requires three.
single starred expressions
Python also has a way for a function to take any number of positional arguments without knowing how many they are. It is the single starred expression (*).
I use a single starred expression (
*) to replace the positional arguments208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 # def unknown_number_of_arguments(x, y, **kwargs): 215 # def unknown_number_of_arguments(x, y, z, **kwargs): 216 def unknown_number_of_arguments(*args, **kwargs): 217 return Nonethe test passes.
unknown_number_of_arguments(0, 1, 2, a=3, b=4, c=5,) -> None └── def unknown_number_of_arguments(*args, **kwargs): └── return None*args, **kwargsis Python convention. I change the names to make it clearer208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 # def unknown_number_of_arguments(x, y, **kwargs): 215 # def unknown_number_of_arguments(x, y, z, **kwargs): 216 # def unknown_number_of_arguments(*args, **kwargs): 217 def unknown_number_of_arguments( 218 *positional_arguments, **keyword_arguments 219 ): 220 return Nonethe test is still green.
how Python treats starred and double starred expressions
I change the return statement because I want the function to return its input (remember the identity function?)
208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 # def unknown_number_of_arguments(x, y, **kwargs): 215 # def unknown_number_of_arguments(x, y, z, **kwargs): 216 # def unknown_number_of_arguments(*args, **kwargs): 217 def unknown_number_of_arguments( 218 *positional_arguments, **keyword_arguments 219 ): 220 return positional_arguments, keyword_arguments 221 # return Nonethe terminal is my friend, and shows AssertionError
AssertionError: assert ((0, 1), {'a': 2, 'b': 3}) == NoneI get a tuple that has
a tuple (anything in parentheses
( )separated by a comma) for the positional argumentsa dictionary (any key-value pairs in curly braces
{ }separated by commas) for the keyword arguments
When
unknown_number_of_arguments(0, 1, a=2, b=3)runsunknown_number_of_arguments( 0, 1, a=2, b=3 ) -> ((0, 1), {'a': 2, 'b': 3}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = (0, 1) ├── keyword_arguments = {'a': 2, 'b': 3} └── return positional_arguments, keyword_arguments return ((0, 1 ), {'a': 2, 'b': 3 })If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple.If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary.
I change my expectation to match reality in the first assertion of test_unknown_number_of_arguments
217 def unknown_number_of_arguments( 218 *positional_arguments, **keyword_arguments 219 ): 220 return positional_arguments, keyword_arguments 221 # return None 222 223 assert_equal( 224 unknown_number_of_arguments( 225 0, 1, a=2, b=3, 226 ), 227 # None 228 ((0, 1), {'a': 2, 'b': 3}) 229 )the terminal is my friend, and shows AssertionError
AssertionError: assert ((0, 1), {'a': 2, 'b': 3, 'c': 4}) == NoneI get a tuple that has
a tuple (anything in parentheses
( )separated by a comma) for the positional arguments.a dictionary (any key-value pairs in curly braces
{ }separated by commas) for the keyword arguments.
When
unknown_number_of_arguments(0, 1, a=2, b=3, c=4)runsunknown_number_of_arguments( 0, 1, a=2, b=3, c=4 ) -> ((0, 1), {'a': 2, 'b': 3, 'c': 4}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = (0, 1) ├── keyword_arguments = {'a': 2, 'b': 3, 'c': 4} └── return positional_arguments, keyword_arguments return ((0, 1 ), {'a': 2, 'b': 3, 'c':4})If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces{ }separated by commas).
I change my expectation to match reality in the second assertion of test_unknown_number_of_arguments
223 assert_equal( 224 unknown_number_of_arguments( 225 0, 1, a=2, b=3, 226 ), 227 # None 228 ((0, 1), {'a': 2, 'b': 3}) 229 ) 230 231 assert_equal( 232 unknown_number_of_arguments( 233 0, 1, a=2, b=3, c=4, 234 ), 235 # () 236 # None 237 ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 238 )the terminal is my friend, and shows AssertionError
AssertionError: assert ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) == NoneI get a tuple that has
a tuple for the positional arguments
a dictionary for the keyword arguments
unknown_number_of_arguments( 0, 1, 2, a=3, b=4, c=5 ) -> ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = (0, 1, 2) ├── keyword_arguments = {'a': 3, 'b': 4, 'c':5} └── return positional_arguments, keyword_arguments return ((0, 1, 2 ), {'a': 3, 'b': 4, 'c':5})If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces{ }separated by commas).
I change my expectation to match reality in the last assertion
231 assert_equal( 232 unknown_number_of_arguments( 233 0, 1, a=2, b=3, c=4, 234 ), 235 # () 236 # None 237 ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 238 ) 239 240 assert_equal( 241 unknown_number_of_arguments( 242 0, 1, 2, a=3, b=4, c=5, 243 ), 244 # None 245 ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 246 ) 247 248 249# Exceptions seenthe test passes.
how Python treats starred expressions
I add variables for the tuple and dictionary of the first assertion
208def test_unknown_number_of_arguments(): 209 # def unknown_number_of_arguments(): 210 # def unknown_number_of_arguments(a): 211 # def unknown_number_of_arguments(**kwargs): 212 # def unknown_number_of_arguments(**kwargs, x): 213 # def unknown_number_of_arguments(x, **kwargs): 214 # def unknown_number_of_arguments(x, y, **kwargs): 215 # def unknown_number_of_arguments(x, y, z, **kwargs): 216 # def unknown_number_of_arguments(*args, **kwargs): 217 def unknown_number_of_arguments( 218 *positional_arguments, **keyword_arguments 219 ): 220 return positional_arguments, keyword_arguments 221 # return None 222 223 a_tuple = (0, 1) 224 a_dictionary = {'a': 2, 'b': 3} 225 assert_equal( 226 unknown_number_of_arguments( 227 0, 1, a=2, b=3, 228 ), 229 # None 230 ((0, 1), {'a': 2, 'b': 3}) 231 )I use the variables to remove repetition of the tuple and dictionary from the first assertion in test_unknown_number_of_arguments
223 a_tuple = (0, 1) 224 a_dictionary = {'a': 2, 'b': 3} 225 assert_equal( 226 unknown_number_of_arguments( 227 # 0, 1, a=2, b=3, 228 a_tuple, a_dictionary 229 ), 230 # None 231 # ((0, 1), {'a': 2, 'b': 3}) 232 (a_tuple, a_dictionary) 233 )the terminal is my friend, and shows AssertionError
AssertionError: assert (((0, 1), {'a... 'b': 3}), {}) == ((0, 1), {'a': 2, 'b': 3})because passing in the values this way means I am sending in two positional arguments (
a_tupleanda_dictionary) so I get a tuple witha tuple of the arguments since they are both positional.
an empty dictionary since there are no keyword arguments.
├── a_tuple = (0, 1) ├── a_dictionary = {'a': 2, 'b': 3} └── unknown_number_of_arguments( a_tuple, a_dictionary ) -> ((0, 1), {'a': 2, 'b': 3}), {}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = (a_tuple, a_dictionary) ├── keyword_arguments = {} └── return positional_arguments , keyword_arguments return ((a_tuple, a_dictionary) , {}) return ((0, 1), {'a': 2, 'b': 3}), {})I change the tuple with
*so that Python breaks up its contents, allowing them to be used as separate arguments223 a_tuple = (0, 1) 224 a_dictionary = {'a': 2, 'b': 3} 225 assert_equal( 226 unknown_number_of_arguments( 227 # 0, 1, a=2, b=3, 228 # a_tuple, a_dictionary 229 *a_tuple, a_dictionary 230 ), 231 # None 232 # ((0, 1), {'a': 2, 'b': 3}) 233 (a_tuple, a_dictionary) 234 )the terminal is my friend, and shows AssertionError
AssertionError: assert ((0, 1, {'a': 2, 'b': 3}), {}) == ((0, 1), {'a': 2, 'b': 3})I still get a tuple of the arguments since they are both positional arguments, the difference is that the items of the tuple appear separately not as another tuple.
how Python treats double starred expressions
I change the dictionary with
**so that Python breaks up the contents, allowing them to be used as keyword arguments223 a_tuple = (0, 1) 224 a_dictionary = {'a': 2, 'b': 3} 225 assert_equal( 226 unknown_number_of_arguments( 227 # 0, 1, a=2, b=3, 228 # a_tuple, a_dictionary 229 # *a_tuple, a_dictionary 230 *a_tuple, **a_dictionary 231 ), 232 # None 233 # ((0, 1), {'a': 2, 'b': 3}) 234 (a_tuple, a_dictionary) 235 )the test passes
├── a_tuple = (0, 1) ├── a_dictionary = {'a': 2, 'b': 3} └── unknown_number_of_arguments( *a_tuple, **a_dictionary ) -> ((0, 1), {'a': 2, 'b': 3}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = a_tuple ├── keyword_arguments = a_dictionary └── return positional_arguments, keyword_arguments return (a_tuple , a_dictionary ) return ((0, 1) , {'a': 2, 'b': 3} )these three statements are the same
unknown_number_of_arguments(*a_tuple, **a_dictionary ) unknown_number_of_arguments(*(0, 1) , **{'a':2, 'b':3}) unknown_number_of_arguments(0, 1 , a=2, b=3 )If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.
I add a variable for the dictionary of the second assertion in test_unknown_number_of_arguments
223 a_tuple = (0, 1) 224 a_dictionary = {'a': 2, 'b': 3} 225 assert_equal( 226 unknown_number_of_arguments( 227 # 0, 1, a=2, b=3, 228 # a_tuple, a_dictionary 229 # *a_tuple, a_dictionary 230 *a_tuple, **a_dictionary 231 ), 232 # None 233 # ((0, 1), {'a': 2, 'b': 3}) 234 (a_tuple, a_dictionary) 235 ) 236 237 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 238 assert_equal( 239 unknown_number_of_arguments( 240 0, 1, a=2, b=3, c=4, 241 ), 242 # () 243 # None 244 ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 245 )I use the
a_tupleand newa_dictionaryvariables to remove repetition of the tuple and dictionary from the second assertion237 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 238 assert_equal( 239 unknown_number_of_arguments( 240 # 0, 1, a=2, b=3, c=4, 241 a_tuple, a_dictionary, 242 ), 243 # () 244 # None 245 # ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 246 (a_tuple, a_dictionary) 247 )the terminal is my friend, and shows AssertionError
AssertionError: assert (((0, 1), {'a... 'c': 4}), {}) == ((0, 1), {'a'...': 3, 'c': 4})because passing in the values this way means I am sending in two positional arguments (
a_tupleanda_dictionary) so I get a tuple witha tuple of the arguments since they are both positional
an empty dictionary since there are no keyword arguments
├── a_tuple = (0, 1) ├── a_dictionary = {'a': 2, 'b': 3, 'c': 4} └── unknown_number_of_arguments( a_tuple, a_dictionary ) -> ((0, 1), {'a': 2, 'b': 3, 'c': 4}), {}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) ├── positional_arguments = (a_tuple, a_dictionary) ├── keyword_arguments = {} └── return positional_arguments, keyword_arguments return ((a_tuple, a_dictionary), {}) return ((0, 1), {'a': 2, 'b': 3, 'c': 4}), {})I change the dictionary with
**so that Python breaks up the contents, allowing them to be used as keyword arguments237 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 238 assert_equal( 239 unknown_number_of_arguments( 240 # 0, 1, a=2, b=3, c=4, 241 # a_tuple, a_dictionary, 242 a_tuple, **a_dictionary, 243 ), 244 # () 245 # None 246 # ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 247 (a_tuple, a_dictionary) 248 )the terminal is my friend, and shows AssertionError
AssertionError: assert (((0, 1),), {...': 3, 'c': 4}) == ((0, 1), {'a'...': 3, 'c': 4})The tuple I get no longer has an empty dictionary, values from the input dictionary.
I change the tuple with
*so that Python breaks up the contents, allowing them to be used as separate arguments237 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 238 assert_equal( 239 unknown_number_of_arguments( 240 # 0, 1, a=2, b=3, c=4, 241 # a_tuple, a_dictionary, 242 # a_tuple, **a_dictionary, 243 *a_tuple, **a_dictionary, 244 ), 245 # () 246 # None 247 # ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 248 (a_tuple, a_dictionary) 249 )the test passes.
├── a_tuple = (0, 1) ├── a_dictionary = {'a': 2, 'b': 3, 'c': 4} └── unknown_number_of_arguments( *a_tuple, **a_dictionary ) -> (0, 1), {'a': 2, 'b': 3, 'c': 4}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) ├── positional_arguments = a_tuple ├── keyword_arguments = a_dictionary └── return positional_arguments, keyword_arguments return (a_tuple , a_dictionary) return (0, 1), {'a': 2, 'b': 3, 'c': 4})these three statements are the same
unknown_number_of_arguments(*a_tuple, **a_dictionary ) unknown_number_of_arguments(*(0, 1) , **{'a': 2, 'b': 3, 'c': 4}) unknown_number_of_arguments(0, 1 , a=2, b=3, c=4 )If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.
I add variables for the tuple and dictionary of the third assertion in test_unknown_number_of_arguments
237 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 238 assert_equal( 239 unknown_number_of_arguments( 240 # 0, 1, a=2, b=3, c=4, 241 # a_tuple, a_dictionary, 242 # a_tuple, **a_dictionary, 243 *a_tuple, **a_dictionary, 244 ), 245 # () 246 # None 247 # ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 248 (a_tuple, a_dictionary) 249 ) 250 251 a_tuple = (0, 1, 2) 252 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 253 assert_equal( 254 unknown_number_of_arguments( 255 0, 1, 2, a=3, b=4, c=5, 256 ), 257 # None 258 ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 259 ) 260 261 262# Exceptions seenI use the variables to remove repetition of the tuple and dictionary from the third assertion
251 a_tuple = (0, 1, 2) 252 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 253 assert_equal( 254 unknown_number_of_arguments( 255 # 0, 1, 2, a=3, b=4, c=5, 256 a_tuple, a_dictionary 257 ), 258 # None 259 # ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 260 (a_tuple, a_dictionary) 261 ) 262 263 264# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert (((0, 1, 2), ... 'c': 5}), {}) == ((0, 1, 2), {...': 4, 'c': 5})because passing in the values this way means I am sending in two positional arguments (
a_tupleanda_dictionary) so I get a tuple witha tuple of the arguments since they are both positional
an empty dictionary since there are no keyword arguments
├── a_tuple = (0, 1, 2) ├── a_dictionary = {'a': 3, 'b': 4, 'c': 5} └── unknown_number_of_arguments( a_tuple, a_dictionary ) -> ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}), {}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = (a_tuple, a_dictionary) ├── keyword_arguments = {} └── return positional_arguments, keyword_arguments return ((a_tuple, a_dictionary) , {}) return ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}), {})I change the inputs with
*and**so that Python breaks up the contents, allowing them to be used as separate arguments251 a_tuple = (0, 1, 2) 252 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 253 assert_equal( 254 unknown_number_of_arguments( 255 # 0, 1, 2, a=3, b=4, c=5, 256 # a_tuple, a_dictionary 257 *a_tuple, **a_dictionary 258 ), 259 # None 260 # ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 261 (a_tuple, a_dictionary) 262 ) 263 264 265# Exceptions seenthe test passes.
├── a_tuple = (0, 1, 2) ├── a_dictionary = {'a': 3, 'b': 4, 'c': 5} └── unknown_number_of_arguments( *a_tuple, **a_dictionary ) -> ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = a_tuple ├── keyword_arguments = a_dictionary └── return positional_arguments, keyword_arguments return ( a_tuple , a_dictionary ) return ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5})these three statements are the same
unknown_number_of_arguments( *a_tuple , **a_dictionary ) unknown_number_of_arguments( *(0, 1, 2), **{'a': 2, 'b': 3, 'c': 4} ) unknown_number_of_arguments( 0, 1, 2, a=3, b=4, c=5 )If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.
I add an assertion with a call to
unknown_number_of_argumentsusing only positional arguments251 a_tuple = (0, 1, 2) 252 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 253 assert_equal( 254 unknown_number_of_arguments( 255 # 0, 1, 2, a=3, b=4, c=5, 256 # a_tuple, a_dictionary 257 *a_tuple, **a_dictionary 258 ), 259 # None 260 # ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 261 (a_tuple, a_dictionary) 262 ) 263 264 a_tuple = (0, 1, 2, 'n') 265 assert_equal( 266 unknown_number_of_arguments(*a_tuple), 267 () 268 ) 269 270 271# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ((0, 1, 2, 'n'), {}) == ()because passing in the values this way means I am sending in only positional arguments (
*a_tuple) so I get a tuple witha tuple of the positional arguments.
an empty dictionary since there are no keyword arguments.
├── a_tuple = (0, 1, 2, 'n') └── unknown_number_of_arguments( *a_tuple ) -> ((0, 1, 2, 'n'), {}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = a_tuple ├── keyword_arguments = {} └── return positional_arguments, keyword_arguments return ( a_tuple , {} ) return ((0, 1, 2, 'n') , {} )these three statements are the same
unknown_number_of_arguments(*a_tuple ) unknown_number_of_arguments(*(0, 1, 2, 'n')) unknown_number_of_arguments(0, 1, 2, 'n' )If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).
I change my expectation to match reality
264 a_tuple = (0, 1, 2, 'n') 265 assert_equal( 266 unknown_number_of_arguments(*a_tuple), 267 # () 268 (a_tuple, {}) 269 ) 270 271 272# Exceptions seenthe test passes.
I add another assertion to see what happens when I call the function with ONLY keyword arguments
264 a_tuple = (0, 1, 2, 'n') 265 assert_equal( 266 unknown_number_of_arguments(*a_tuple), 267 # () 268 (a_tuple, {}) 269 ) 270 271 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 272 assert_equal( 273 unknown_number_of_arguments(**a_dictionary), 274 () 275 ) 276 277 278# Exceptions seenthe terminal is my friend, and shows
AssertionError: assert ((), {'a': 1,... 3, 'd': 'n'}) == ()because passing in the values this way means I am sending in only keyword arguments (
**a_dictionary) so I get a tuple withan empty tuple since there are no positional arguments
a dictionary of the keyword arguments
├── a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} └── unknown_number_of_arguments( **a_dictionary ) -> ((), {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = () ├── keyword_arguments = { │ 'a': 1, 'b': 2, 'c': 3, 'd': 'n' │ } └── return positional_arguments, keyword_arguments return ((), {'a': 1, 'b': 2, 'c': 3, 'd': 'n'})these three statements are the same
unknown_number_of_arguments(**a_dictionary ) unknown_number_of_arguments({'a': 1, 'b': 2, 'c': 3, 'd': 'n'}) unknown_number_of_arguments( a = 1, b = 2, c = 3, d = 'n' )If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces{ }separated by commas).
I change my expectation to match reality
271 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 272 assert_equal( 273 unknown_number_of_arguments(**a_dictionary), 274 # () 275 ((), a_dictionary) 276 ) 277 278 279# Exceptions seenthe test passes.
I add one more assertion to see what happens when I call the unknown_number_of_arguments function with no inputs
271 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 272 assert_equal( 273 unknown_number_of_arguments(**a_dictionary), 274 # () 275 ((), a_dictionary) 276 ) 277 278 assert_equal( 279 unknown_number_of_arguments(), TypeError 280 ) 281 282 283# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ((), {}) == <class 'TypeError'>because
unknown_number_of_argumentsgets called with no arguments so I get a tuple withan empty tuple since there are no positional arguments.
an empty dictionary since there are no keyword arguments.
I change my expectation to match reality
278 assert_equal( 279 # unknown_number_of_arguments(), TypeError 280 unknown_number_of_arguments(), ((), {}) 281 ) 282 283 284# Exceptions seenthe test passes.
unknown_number_of_arguments() -> ((), {}) └── def unknown_number_of_arguments( *positional_arguments, **keyword_arguments ): ├── positional_arguments = () ├── keyword_arguments = {} └── return positional_arguments, keyword_arguments return (() , {} )If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces{ }separated by commas).
I remove the commented lines from test_unknown_number_of_arguments
208def test_unknown_number_of_arguments(): 209 def unknown_number_of_arguments( 210 *positional_arguments, **keyword_arguments 211 ): 212 return positional_arguments, keyword_arguments 213 214 a_tuple = (0, 1) 215 a_dictionary = {'a': 2, 'b': 3} 216 assert_equal( 217 unknown_number_of_arguments( 218 *a_tuple, **a_dictionary 219 ), 220 (a_tuple, a_dictionary) 221 )223 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 224 assert_equal( 225 unknown_number_of_arguments( 226 *a_tuple, **a_dictionary, 227 ), 228 (a_tuple, a_dictionary) 229 )231 a_tuple = (0, 1, 2) 232 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 233 assert_equal( 234 unknown_number_of_arguments( 235 *a_tuple, **a_dictionary 236 ), 237 (a_tuple, a_dictionary) 238 )240 a_tuple = (0, 1, 2, 'n') 241 assert_equal( 242 unknown_number_of_arguments(*a_tuple), 243 (a_tuple, {}) 244 )246 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 247 assert_equal( 248 unknown_number_of_arguments(**a_dictionary), 249 ((), a_dictionary) 250 ) 251 252 assert_equal( 253 unknown_number_of_arguments(), ((), {}) 254 ) 255 256 257# Exceptions seen 258# AssertionError 259# NameError 260# TypeError 261# SyntaxErrorI add a git commit message in the other terminal
git commit --all --message \ 'add test_unknown_number_of_arguments'the terminal shows a summary of the changes then goes back to the command line.
I can make a function that can take any number of positional or keyword arguments.
close the project
I close
test_functions.pyI click in the terminal where the tests are running
I use q on the keyboard to leave the tests. The terminal goes back to the command line.
I change directory to the parent of
functionscd ..the terminal shows
.../pumping_pythonI am back in the
pumping_pythondirectory.
review
I ran tests to show that I can make functions that take input
I can call a function with input by placing an input in parentheses when I call it.
The identity or passthrough function returns its input as output.
I can call a function with positional and keyword arguments.
I can make a function that can take any number of positional or keyword arguments
If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces{ }separated by commas).
code from the chapter
what is next?
I am going for a walk. Would you like to test TypeError?
rate pumping python
If this has been a 7 star experience for you, please CLICK HERE to leave a 5 star review of pumping python. It helps other people get into the book too.