functions that take input
To review, 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
f(x) -> y
function(input_object) -> output_object
solar_panel(sunlight) -> electricity
factory(materials) -> product
chef(ingredients) -> food
stomach(food) -> poop
the function does something (the process) with input_object and returns output_object as the result. For example
how to make a function that takes input
functions are made with
the def keyword
a name
parentheses with the inputs allowed, and a colon at the end
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 test_making_a_function_w_pass():
2 def w_pass():
3 pass
4
5 assert w_pass() is None
6
7
8def test_making_a_function_w_return():
9 def w_return():
10 return
11
12 assert w_return() is None
13
14
15def test_making_a_function_w_return_none():
16 def w_return_none():
17 return None
18
19 assert w_return_none() is None
20
21
22def test_what_happens_after_functions_return():
23 def return_leaves_the_function():
24 return None
25 return 'only one way for this line to run'
26
27 assert return_leaves_the_function() is None
28
29
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 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 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
61def positional_arguments(first_input, last_input):
62 return first_input, last_input
63
64
65def test_positional_arguments():
66 first, last = 'first', 'last'
67
68 assert (
69 positional_arguments(first, last)
70 == (first, last)
71 )
72 assert (
73 positional_arguments(last, first)
74 == (last, first)
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 a_set = {0, 1, 2, 'n'}
89 a_dictionary = {'key': 'value'}
90 assert (
91 keyword_arguments(
92 a_set, a_dictionary,
93 )
94 == (a_set, a_dictionary)
95 )
96
97
98def keyword_arguments(first_input, last_input):
99 return first_input, last_input
100
101def test_keyword_arguments():
102 first, last = 'first', 'last'
103
104 assert (
105 keyword_arguments(
106 first_input=first, last_input=last,
107 )
108 == (first, last)
109 )
110 assert (
111 keyword_arguments(
112 last_input=last, first_input=first,
113 )
114 == (first, last)
115 )
116 assert (
117 keyword_arguments(
118 last_input=0, first_input=1,
119 )
120 == (1, 0)
121 )
122
123 a_tuple = (0, 1, 2, 'n')
124 a_list = [0, 1, 2, 'n']
125 assert (
126 keyword_arguments(
127 first_input=a_tuple,
128 last_input=a_list,
129 )
130 == (a_tuple, a_list)
131 )
132
133 a_set = {0, 1, 2, 'n'}
134 a_dictionary = {'key': 'value'}
135 assert (
136 positional_arguments(
137 last_input=a_dictionary,
138 first_input=a_set,
139 )
140 == (a_set, a_dictionary)
141 )
142
143
144def test_args_and_kwargs():
145 def args_and_kwargs(first_input, last_input):
146 return first_input, last_input
147
148 first, last = 'first', 'last'
149
150 assert (
151 args_and_kwargs(
152 first, last_input=last,
153 )
154 == (first, last)
155 )
156
157
158def test_optional_arguments():
159 def optional_arguments(
160 first_input, last_input='doe',
161 ):
162 return first_input, last_input
163
164 first_name, last_name = 'jane', 'doe'
165
166 assert (
167 optional_arguments(
168 first_name,
169 )
170 == (first_name, last_name)
171 )
172
173 first_name, blow = 'joe', 'blow'
174 assert (
175 optional_arguments(
176 first_name, blow
177 )
178 == (first_name, blow)
179 )
180
181 first_name = 'john'
182 assert (
183 optional_arguments(
184 first_input=first_name
185 )
186 == (first_name, last_name)
187 )
188
189 last_name = 'smith'
190 assert (
191 optional_arguments(
192 last_input=last_name,
193 first_input=first_name,
194 )
195 == (first_name, last_name)
196 )
197
198
199def test_unknown_number_of_arguments():
200 def unknown_number_of_arguments(
201 *positional_arguments, **keyword_arguments
202 ):
203 return positional_arguments, keyword_arguments
204
205 a_tuple = (0, 1)
206 a_dictionary = {'a': 2, 'b': 3}
207 assert (
208 unknown_number_of_arguments(
209 *a_tuple, **a_dictionary
210 )
211 == (a_tuple, a_dictionary)
212 )
213
214 a_tuple = (0, 1)
215 a_dictionary = {'a': 2, 'b': 3, 'c': 4}
216 assert (
217 unknown_number_of_arguments(
218 *a_tuple, **a_dictionary
219 )
220 == (a_tuple, a_dictionary)
221 )
222
223 a_tuple = (0, 1, 2)
224 a_dictionary = {'a': 3, 'b': 4, 'c': 5}
225 assert (
226 unknown_number_of_arguments(
227 *a_tuple, **a_dictionary
228 )
229 == (a_tuple, a_dictionary)
230 )
231
232 a_tuple = (0, 1, 2, 'n')
233 assert (
234 unknown_number_of_arguments(*a_tuple)
235 == (a_tuple, {})
236 )
237
238 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
239 assert (
240 unknown_number_of_arguments(**a_dictionary)
241 == ((), a_dictionary)
242 )
243
244 assert (
245 unknown_number_of_arguments()
246 == ((), {})
247 )
248
249
250# Exceptions seen
251# AssertionError
252# NameError
253# TypeError
254# SyntaxError
questions about functions that take input
Questions to think about as I go through the chapter
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.pyI 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 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
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 totest_identity_functionuses 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 == objectbecause 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 am not all the way genius, yet.
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 identity(object) -> objectI 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.
The Identity Function returns its input as output.
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 one45def 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 + numberWhen
add_x(0)runsadd_x(0) 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 + 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.
I can use a function to remove repetition. Is there a better way to handle the changing results?
test_positional_arguments
test_identity_function used one input, these next tests use functions that take more than one input.
RED: make it fail
I go back to the terminal where the tests are running
I add a test
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.
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 totest_positional_argumentsuses 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 make the function take input by adding a name in parentheses
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) -> 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 totest_positional_argumentsuses 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, and adding a name in 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) -> 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_inputpositional_arguments(first_input, last_input) -> first_input, last_inputWhen
positional_arguments('first', 'last')runspositional_arguments('first', 'last') def positional_arguments(first_input, last_input) ├── first_input = 'first' ├── last_input = 'last' └── return first_input, last_input return 'first' , 'last'The problem with giving arguments this way is that they always have to be in the order in the function definition or I get something different. The advantage with 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 the call in this assertion sends'last'asfirst_inputand'first'aslast_input. Using substitution since I can treat a call to a function as the object it returnspositional_arguments(first_input, last_input) -> first_input, last_inputWhen
positional_arguments('last', 'first')runspositional_arguments('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
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'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 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'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 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
80 assert ( 81 # positional_arguments('last', 'first') 82 # == ('first', 'last') 83 # == ('last', 'first') 84 positional_arguments(last, first) 85 == (last, first) 86 ) 87 assert ( 88 positional_arguments(0, 1) 89 == (1, 0) 90 ) 91 92 93# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (0, 1) == (1, 0)because the function always returns
first_input, last_inputand the call in this assertion sends0asfirst_inputand1aslast_inputpositional_arguments(first_input, last_input) -> first_input, last_inputWhen
positional_arguments(0, 1)runspositional_arguments(0, 1) def positional_arguments(first_input, last_input) ├── first_input = 0 ├── last_input = 1 └── return first_input, last_input return 0 , 1Using 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
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 assert ( 88 positional_arguments(0, 1) 89 # == (1, 0) 90 == (0, 1) 91 ) 92 93 94# Exceptions seenthe test passes.
I add an assertion with a tuple (anything in parentheses
( )separated by a comma) and a list(anything in square brackets ‘[ ]’)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 assert ( 88 positional_arguments(0, 1) 89 # == (1, 0) 90 == (0, 1) 91 ) 92 93 a_tuple = (0, 1, 2, 'n') 94 a_list = [0, 1, 2, 'n'] 95 assert ( 96 positional_arguments(a_list, a_tuple) 97 == (a_tuple, a_list) 98 ) 99 100 101# 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_inputpositional_arguments(first_input, last_input) -> first_input, last_inputa_tuple = (0, 1, 2, 'n') a_list = [0, 1, 2, 'n']When
positional_arguments(a_tuple, a_list)runspositional_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_listUsing 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.
I 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 assert ( 76 positional_arguments(0, 1) 77 == (0, 1) 78 ) 79 80 a_tuple = (0, 1, 2, 'n') 81 a_list = [0, 1, 2, 'n'] 82 assert ( 83 positional_arguments(a_tuple, a_list) 84 == (a_tuple, a_list) 85 ) 86 87 88# 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.
test_keyword_arguments
The last test 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 new test to
test_functions.py80 a_tuple = (0, 1, 2, 'n') 81 a_list = [0, 1, 2, 'n'] 82 assert ( 83 positional_arguments(a_tuple, a_list) 84 == (a_tuple, a_list) 85 ) 86 87 88def test_keyword_arguments(): 89 assert keyword_arguments() == None 90 91 92# 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
88def test_keyword_arguments():
89 def keyword_arguments():
90 return None
91
92 assert keyword_arguments() == None
93
94
95# Exceptions seen
the test passes.
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.
RED: make it fail
I add input to the function call with a name
88def test_keyword_arguments(): 89 def keyword_arguments(): 90 return None 91 92 # assert keyword_arguments() == None 93 assert keyword_arguments(first_input='first') == None 94 95 96# 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 totest_keyword_argumentsuses 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 make the function take input by adding a name in parentheses
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 def keyword_arguments(the_input): 91 return None 92 93 # assert keyword_arguments() == None 94 assert keyword_arguments(first_input='first') == None 95 96 97# 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 to match the one used in the call
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 def keyword_arguments(first_input): 92 return None 93 94 # assert keyword_arguments() == None 95 assert keyword_arguments(first_input='first') == None 96 97 98# Exceptions seenthe test passes because I use the same name in the function definition when I call it with a keyword argument.
I add another input to the function call
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 def keyword_arguments(first_input): 92 return None 93 94 # assert keyword_arguments() == None 95 # assert keyword_arguments(first_input='first') == None 96 assert ( 97 keyword_arguments( 98 first_input='first', last_input='last', 99 ) 100 == None 101 ) 102 103 104# 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 totest_keyword_argumentsuses 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
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 # def keyword_arguments(first_input): 92 def keyword_arguments(first_input, second_input): 93 return None 94 95 # assert keyword_arguments() == None 96 # assert keyword_arguments(first_input='first') == None 97 assert ( 98 keyword_arguments( 99 first_input='first', last_input='last', 100 ) 101 == None 102 ) 103 104 105# Exceptions seenthe terminal still shows TypeError because the names in the function call and function definition are different.
I change name of the input to match the one used in the call
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 # def keyword_arguments(first_input): 92 # def keyword_arguments(first_input, second_input): 93 def keyword_arguments(first_input, last_input): 94 return None 95 96 # assert keyword_arguments() == None 97 # assert keyword_arguments(first_input='first') == None 98 assert ( 99 keyword_arguments( 100 first_input='first', last_input='last', 101 ) 102 == None 103 ) 104 105 106# Exceptions seenthe test passes because I use the same name in the function definition when I call it with a keyword argument.
I change the expectation of the assertion
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 # def keyword_arguments(first_input): 92 # def keyword_arguments(first_input, the_input): 93 def keyword_arguments(first_input, last_input): 94 return None 95 96 # assert keyword_arguments() == None 97 # assert keyword_arguments(first_input='first') == None 98 assert ( 99 keyword_arguments( 100 first_input='first', last_input='last', 101 ) 102 # == None 103 == ('first', 'last') 104 ) 105 106 107# 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 returnsassert keyword_arguments(first_input='first', last_input='last') == ('first', 'last') 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)
88def test_keyword_arguments(): 89 # def keyword_arguments(): 90 # def keyword_arguments(the_input): 91 # def keyword_arguments(first_input): 92 # def keyword_arguments(first_input, the_input): 93 def keyword_arguments(first_input, last_input): 94 # return None 95 return first_input, last_input 96 97 # assert keyword_arguments() == None 98 # assert keyword_arguments(first_input='first') == None 99 assert ( 100 keyword_arguments( 101 first_input='first', last_input='last', 102 ) 103 # == None 104 == ('first', 'last') 105 ) 106 107 108# 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, last_input) -> first_input, last_inputWhen
keyword_arguments(first_input='first', last_input='last')runskeyword_arguments(first_input='first', last_input='last') def keyword_arguments(first_input, last_input) ├── first_input = 'first' ├── last_input = 'last' └── return first_input, last_input return 'first' , 'last'The problem with giving arguments this way is I must use the exact names. The advantage of giving arguments this way is that they do not have to match the order in the function definition. I add an assertion with the keyword arguments given out of order
97 # assert keyword_arguments() == None 98 # assert keyword_arguments(first_input='first') == None 99 assert ( 100 keyword_arguments( 101 first_input='first', last_input='last', 102 ) 103 # == None 104 == ('first', 'last') 105 ) 106 assert ( 107 keyword_arguments( 108 last_input='last', first_input='first', 109 ) 110 == ('last', 'first') 111 ) 112 113 114# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('first', 'last') == ('last', 'first')because the function always returns
first_input, last_inputand the call in this assertion sends'last'aslast_inputand'first'asfirst_input, the order does not matter because I used the names.keyword_arguments(first_input, last_input) -> first_input, last_inputWhen
keyword_arguments(last_input='last', first_input='first')runskeyword_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'Using substitution since I can treat a call to a function as the object it returns
assert keyword_arguments(last_input='last', first_input='first') == ('last', 'first') assert ('first', 'last') == ('last', 'first')I change my expectation to match reality
97 # assert keyword_arguments() == None 98 # assert keyword_arguments(first_input='first') == None 99 assert ( 100 keyword_arguments( 101 first_input='first', last_input='last', 102 ) 103 # == None 104 == ('first', 'last') 105 ) 106 assert ( 107 keyword_arguments( 108 last_input='last', first_input='first', 109 ) 110 # == ('last', 'first') 111 == ('first', 'last') 112 ) 113 114 115# Exceptions seenI add variables for
'first'and'last'97 # assert keyword_arguments() == None 98 # assert keyword_arguments(first_input='first') == None 99 100 first, last = 'first', 'last' 101 102 assert ( 103 keyword_arguments( 104 first_input='first', last_input='last', 105 ) 106 # == None 107 == ('first', 'last') 108 ) 109 assert ( 110 keyword_arguments( 111 last_input='last', first_input='first', 112 ) 113 # == ('last', 'first') 114 == ('first', 'last') 115 ) 116 117 118# Exceptions seenI use the variables to remove repetition of
'first'and'last'97 # assert keyword_arguments() == None 98 # assert keyword_arguments(first_input='first') == None 99 100 first, last = 'first', 'last' 101 102 assert ( 103 keyword_arguments( 104 # first_input='first', last_input='last', 105 first_input=first, last_input=last, 106 ) 107 # == None 108 # == ('first', 'last') 109 == (first, last) 110 ) 111 assert ( 112 keyword_arguments( 113 # last_input='last', first_input='first', 114 last_input=last, first_input=first, 115 ) 116 # == ('last', 'first') 117 # == ('first', 'last') 118 == (first, last) 119 ) 120 121 122# Exceptions seenthe test is still green.
I add another assertion
111 assert ( 112 keyword_arguments( 113 # last_input='last', first_input='first', 114 last_input=last, first_input=first, 115 ) 116 # == ('last', 'first') 117 # == ('first', 'last') 118 == (first, last) 119 ) 120 assert ( 121 keyword_arguments( 122 last_input=0, first_input=1, 123 ) 124 == (0, 1) 125 ) 126 127 128# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert (1, 0) == (0, 1)because the function always returns
first_input, last_inputand the call in this assertion sends0aslast_inputand1asfirst_input, the order does not matter because I used the names.When
keyword_arguments(last_input=0, first_input=1)runskeyword_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 , 0Using substitution since I can treat a call to a function as the object it returns
assert keyword_arguments(last_input=0, first_input=1) == (0, 1) assert (1, 0) == (0, 1)I change my expectation to match reality
120 assert ( 121 keyword_arguments( 122 last_input=0, first_input=1, 123 ) 124 # == (0, 1) 125 == (1, 0) 126 ) 127 128 129# Exceptions seenthe test passes.
I add an assertion
120 assert ( 121 keyword_arguments( 122 last_input=0, first_input=1, 123 ) 124 # == (0, 1) 125 == (1, 0) 126 ) 127 128 a_tuple = (0, 1, 2, 'n') 129 a_list = [0, 1, 2, 'n'] 130 assert ( 131 keyword_arguments( 132 first_input=a_list, 133 last_input=a_tuple, 134 ) 135 == (a_tuple, a_list) 136 ) 137 138 139# 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 assertion sends[0, 1, 2, 'n']asfirst_inputand(0, 1, 2, 'n')aslast_input, the order does not matter because I used the nameskeyword_arguments(first_input, last_input) -> first_input, last_inputa_tuple = (0, 1, 2, 'n') a_list = [0, 1, 2, 'n']When
keyword_arguments(first_input=a_list, last_input=a_tuple)runskeyword_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_tuple , a_listUsing substitution since I can treat a call to a function as the object it returns
assert keyword_arguments( first_input=[0, 1, 2, 'n'], last_input=(0, 1, 2, 'n') ) == ((0, 1, 2, 'n'), [0, 1, 2, 'n']) assert [0, 1, 2, 'n'], (0, 1, 2, 'n') == ((0, 1, 2, 'n'), [0, 1, 2, 'n'])I change reality to match my expectation
128 a_tuple = (0, 1, 2, 'n') 129 a_list = [0, 1, 2, 'n'] 130 assert ( 131 keyword_arguments( 132 # first_input=a_list, 133 # last_input=a_tuple, 134 first_input=a_tuple, 135 last_input=a_list, 136 ) 137 == (a_tuple, a_list) 138 ) 139 140 141# Exceptions seenthe test passes.
keyword_argumentsandpositional_argumentsare the same functions, 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
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 add an assertion to show that the two functions are the same, by calling the positional_arguments function with keyword arguments
128 a_tuple = (0, 1, 2, 'n') 129 a_list = [0, 1, 2, 'n'] 130 assert ( 131 keyword_arguments( 132 # first_input=a_list, 133 # last_input=a_tuple, 134 first_input=a_tuple, 135 last_input=a_list, 136 ) 137 == (a_tuple, a_list) 138 ) 139 140 a_set = {0, 1, 2, 'n'} 141 a_dictionary = {'key': 'value'} 142 assert ( 143 positional_arguments( 144 last_input=a_dictionary, 145 first_input=a_set, 146 ) 147 == (a_set, a_dictionary) 148 ) 149 150 151# 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
58 assert add_x(9) == 12 59 60 61def positional_arguments(first_input, last_input): 62 return first_input, last_input 63 64 65def test_positional_arguments(): 66 first, last = 'first', 'last' 67 68 assert ( 69 positional_arguments(first, last) 70 == (first, last) 71 ) 72 assert ( 73 positional_arguments(last, first) 74 == (last, first) 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 89def test_keyword_arguments():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, ) 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) 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
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 a_set = {0, 1, 2, 'n'} 89 a_dictionary = {'key': 'value'} 90 assert ( 91 keyword_arguments( 92 a_set, a_dictionary, 93 ) 94 == (a_set, a_dictionary) 95 ) 96 97 98def 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
88 a_set = {0, 1, 2, 'n'} 89 a_dictionary = {'key': 'value'} 90 assert ( 91 keyword_arguments( 92 a_set, a_dictionary, 93 ) 94 == (a_set, a_dictionary) 95 ) 96 97 98def keyword_arguments(first_input, last_input): 99 return first_input, last_input 100 101 102def test_keyword_arguments():102def test_keyword_arguments(): 103 # def keyword_arguments(): 104 # def keyword_arguments(the_input): 105 # def keyword_arguments(first_input): 106 # def keyword_arguments(first_input, the_input): 107 # return None 108 109 # assert keyword_arguments() == None 110 # assert keyword_arguments(first_input='first') == None 111 112 first, last = 'first', 'last'the 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) 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, ) 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
88 a_set = {0, 1, 2, 'n'} 89 a_dictionary = {'key': 'value'} 90 assert ( 91 keyword_arguments( 92 a_set, a_dictionary, 93 ) 94 == (a_set, a_dictionary) 95 ) 96 97 98def keyword_arguments(first_input, last_input):102def test_keyword_arguments(): 103 first, last = 'first', 'last' 104 105 assert ( 106 keyword_arguments( 107 first_input=first, last_input=last, 108 ) 109 == (first, last) 110 )111 assert ( 112 keyword_arguments( 113 last_input=last, first_input=first, 114 ) 115 == (first, last) 116 )117 assert ( 118 keyword_arguments( 119 last_input=0, first_input=1, 120 ) 121 == (1, 0) 122 )124 a_tuple = (0, 1, 2, 'n') 125 a_list = [0, 1, 2, 'n'] 126 assert ( 127 keyword_arguments( 128 first_input=a_tuple, 129 last_input=a_list, 130 ) 131 == (a_tuple, a_list) 132 )134 a_set = {0, 1, 2, 'n'} 135 a_dictionary = {'key': 'value'} 136 assert ( 137 positional_arguments( 138 last_input=a_dictionary, 139 first_input=a_set, 140 ) 141 == (a_set, a_dictionary) 142 ) 143 144 145# 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
136 assert ( 137 positional_arguments( 138 last_input=a_dictionary, 139 first_input=a_set, 140 ) 141 == (a_set, a_dictionary) 142 ) 143 144 145def test_args_and_kwargs(): 146 assert ( 147 args_and_kwargs( 148 last_input='last', 'first', 149 ) 150 == ('first', 'last') 151 ) 152 153 154# 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.py153# Exceptions seen 154# AssertionError 155# NameError 156# TypeError 157# SyntaxErrorI change the order of the arguments to follow Python rules
145def test_args_and_kwargs(): 146 assert ( 147 args_and_kwargs( 148 # last_input='last', 'first', 149 'first', last_input='last', 150 ) 151 == ('first', 'last') 152 ) 153 154 155# 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
145def test_args_and_kwargs(): 146 def args_and_kwargs(): 147 return None 148 149 assert ( 150 args_and_kwargs( 151 # last_input='last', 'first', 152 'first', last_input='last', 153 ) 154 == ('first', 'last') 155 ) 156 157 158# 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 totest_args_and_kwargsuses 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 the name in parentheses
145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 def args_and_kwargs(last_input): 148 return None 149 150 assert ( 151 args_and_kwargs( 152 # last_input='last', 'first', 153 'first', last_input='last', 154 ) 155 == ('first', 'last') 156 ) 157 158 159# 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 with positional and keyword arguments ('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 another name in parentheses to make it clearer
145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 # def args_and_kwargs(last_input): 148 def args_and_kwargs(last_input, first_input): 149 return None 150 151 assert ( 152 args_and_kwargs( 153 # last_input='last', 'first', 154 'first', last_input='last', 155 ) 156 == ('first', 'last') 157 ) 158 159 160# Exceptions seenthe terminal still shows TypeError because I have not fixed the problem, I gave confusing values in the call. Python still cannot tell the difference between the two values because I gave a positional argument which from the function definition is
last_inputand 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 twice145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 # def args_and_kwargs(last_input): 148 # def args_and_kwargs(last_input, first_input): 149 def args_and_kwargs(last_input, last_input): 150 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
145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 # def args_and_kwargs(last_input): 148 # def args_and_kwargs(last_input, first_input): 149 # def args_and_kwargs(last_input, last_input): 150 def args_and_kwargs(first_input, last_input): 151 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. Using substitutionassert args_and_kwargs('first', last_input='last') == ('first', 'last') assert None == ('first', 'last')which raises AssertionError since None is NOT equal to a tuple.
I change the return statement to give the test what it wants
145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 # def args_and_kwargs(last_input): 148 # def args_and_kwargs(last_input, first_input): 149 # def args_and_kwargs(last_input, last_input): 150 def args_and_kwargs(first_input, last_input): 151 # return None 152 return first_input, last_input 153 154 assert ( 155 args_and_kwargs( 156 # last_input='last', 'first', 157 'first', last_input='last', 158 ) 159 == ('first', 'last') 160 ) 161 162 163# Exceptions seenthe test passes.
args_and_kwargs(first_input, last_input) -> first_input, last_inputI add variables for
'first'and'last'145def test_args_and_kwargs(): 146 # def args_and_kwargs(): 147 # def args_and_kwargs(last_input): 148 # def args_and_kwargs(last_input, first_input): 149 # def args_and_kwargs(last_input, last_input): 150 def args_and_kwargs(first_input, last_input): 151 # return None 152 return first_input, last_input 153 154 first, last = 'first', 'last'I use the new variables to remove repetition of
'first'and'last'154 first, last = 'first', 'last' 155 156 assert ( 157 args_and_kwargs( 158 # last_input='last', 'first', 159 # 'first',last_input='last', 160 first, last_input=last, 161 ) 162 # == ('first', 'last') 163 == (first, last) 164 ) 165 166 167# Exceptions seenthe test is still green.
I remove the commented lines
145def test_args_and_kwargs(): 146 def args_and_kwargs(first_input, last_input): 147 return first_input, last_input 148 149 first, last = 'first', 'last' 150 151 assert ( 152 args_and_kwargs( 153 first, last_input=last, 154 ) 155 == (first, last) 156 ) 157 158 159# 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.py151 assert ( 152 args_and_kwargs( 153 first, last_input=last, 154 ) 155 == (first, last) 156 ) 157 158 159def test_optional_arguments(): 160 first_name, last_name = 'jane', 'doe' 161 assert ( 162 optional_arguments( 163 first_name, last_input=last_name, 164 ) 165 == (first_name, last_name) 166 ) 167 168 169# 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
159def test_optional_arguments():
160 def optional_arguments(first_input, last_input):
161 return first_input, last_input
162
163 first_name, last_name = 'jane', 'doe'
164 assert (
165 optional_arguments(
166 first_name, last_input=last_name,
167 )
168 == (first_name, last_name)
169 )
170
171
172# 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 argument159def test_optional_arguments(): 160 def optional_arguments(first_input, last_input): 161 return first_input, last_input 162 163 first_name, last_name = 'jane', 'doe' 164 assert ( 165 optional_arguments( 166 # first_name, last_input=last_name, 167 first_name, 168 ) 169 == (first_name, last_name) 170 ) 171 172 173# 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
159def test_optional_arguments(): 160 # def optional_arguments(first_input, last_input): 161 def optional_arguments( 162 first_input, last_input='doe', 163 ): 164 return first_input, last_input 165 166 first_name, last_name = 'jane', 'doe' 167 assert ( 168 optional_arguments( 169 # first_name, last_input=last_name, 170 first_name, 171 ) 172 == (first_name, last_name) 173 ) 174 175 176# 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).optional_arguments(first_input, last_input='doe') -> (first_input, last_input )it means these two calls are the same
optional_arguments('jane') optional_arguments('jane', last_input='doe')When
optional_arguments('jane')runsoptional_arguments('jane') def optional_arguments(first_input, last_input='doe') ├── first_input = 'jane' ├── last_input = 'doe' └── return first_input, last_input return 'jane' , 'doe'When
optional_arguments(first_input, last_input='doe')runsoptional_arguments('jane', last_input='doe') def optional_arguments(first_input, last_input='doe') ├── first_input = 'jane' ├── last_input = 'doe' └── 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 call the function with different values
168 assert ( 169 optional_arguments( 170 # first_name, last_input=last_name, 171 first_name, 172 ) 173 == (first_name, last_name) 174 ) 175 176 first_name, blow = 'joe', 'blow' 177 assert ( 178 optional_arguments( 179 first_name, blow 180 ) 181 == () 182 ) 183 184 185# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('joe', 'blow') == ()I change my expectation to match reality
176 first_name, blow = 'joe', 'blow' 177 assert ( 178 optional_arguments( 179 first_name, blow 180 ) 181 # == () 182 == (first_name, blow) 183 ) 184 185 186# Exceptions seenthe test passes
optional_arguments(first_input, last_input='doe') -> (first_input, last_input )first_name, blow = 'joe', 'blow'When
optional_arguments(first_name, blow)runsoptional_arguments(first_name, last_name) def optional_arguments(first_input, last_input='doe') ├── first_input = first_name ├── last_input = blow # because a value is given in the call └── return first_input, last_input return 'joe' , 'blow'I add another assertion
176 first_name, blow = 'joe', 'blow' 177 assert ( 178 optional_arguments( 179 first_name, blow 180 ) 181 # == () 182 == (first_name, blow) 183 ) 184 185 first_name = 'john' 186 assert ( 187 optional_arguments( 188 first_input=first_name 189 ) 190 == () 191 ) 192 193 194# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('john', 'doe') == ()I change my expectation to match reality
185 first_name = 'john' 186 assert ( 187 optional_arguments( 188 first_input=first_name 189 ) 190 # == () 191 == (first_name, last_name) 192 ) 193 194 195# 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') optional_arguments(first_input, last_input='doe')is the same as
optional_arguments('john' , last_input='doe') optional_arguments(first_input, last_input='doe')A function uses the default value for a parameter when it is called without the parameter.
I add one more assertion
185 first_name = 'john' 186 assert ( 187 optional_arguments( 188 first_input=first_name 189 ) 190 # == () 191 == (first_name, last_name) 192 ) 193 194 last_name = 'smith' 195 assert ( 196 optional_arguments( 197 last_input=last_name, 198 first_input=first_name, 199 ) 200 == (last_name, first_name) 201 ) 202 203 204# Exceptions seenthe terminal is my friend, and shows AssertionError
AssertionError: assert ('john', 'smith') == ('smith', 'john')I change my expectation to match reality
194 last_name = 'smith' 195 assert ( 196 optional_arguments( 197 last_input=last_name, 198 first_input=first_name, 199 ) 200 # == (last_name, first_name) 201 == (first_name, last_name) 202 ) 203 204 205# Exceptions seenthe test passes.
I remove the commented lines
159def test_optional_arguments(): 160 def optional_arguments( 161 first_input, last_input='doe', 162 ): 163 return first_input, last_input 164 165 first_name, last_name = 'jane', 'doe' 166 167 assert ( 168 optional_arguments( 169 first_name, 170 ) 171 == (first_name, last_name) 172 )174 first_name, blow = 'joe', 'blow' 175 assert ( 176 optional_arguments( 177 first_name, blow 178 ) 179 == (first_name, blow) 180 )182 first_name = 'john' 183 assert ( 184 optional_arguments( 185 first_input=first_name 186 ) 187 == (first_name, last_name) 188 )190 last_name = 'smith' 191 assert ( 192 optional_arguments( 193 last_input=last_name, 194 first_input=first_name, 195 ) 196 == (first_name, last_name) 197 ) 198 199 200# 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
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 a new test to
test_functions.py190 last_name = 'smith' 191 assert ( 192 optional_arguments( 193 last_input=last_name, 194 first_input=first_name, 195 ) 196 == (first_name, last_name) 197 ) 198 199 200def test_unknown_number_of_arguments(): 201 assert ( 202 unknown_number_of_arguments( 203 0, 1, a=2, b=3, 204 ) 205 == None 206 ) 207 208 209# 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
200def test_unknown_number_of_arguments(): 201 def unknown_number_of_arguments(): 202 return None 203 204 assert ( 205 unknown_number_of_arguments( 206 0, 1, a=2, b=3, 207 ) 208 == None 209 ) 210 211 212# 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 the name to the function definition
200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 def unknown_number_of_arguments(a): 203 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.
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 expressions (**).
I use a double starred expression to replace
ain the parentheses200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 def unknown_number_of_arguments(**kwargs): 204 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 a name for the first positional argument
200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 def unknown_number_of_arguments(**kwargs, x): 205 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_arguments200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 def unknown_number_of_arguments(x, **kwargs): 206 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 a name for the other positional argument
200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 def unknown_number_of_arguments(x, y, **kwargs): 207 return None 208 209 assert ( 210 unknown_number_of_arguments( 211 0, 1, a=2, b=3, 212 ) 213 == None 214 ) 215 216 217# Exceptions seenthe test passes.
REFACTOR: make it better
I add an assertion to see what happens if I call the function with 3 keyword arguments
209 assert ( 210 unknown_number_of_arguments( 211 0, 1, a=2, b=3, 212 ) 213 == None 214 ) 215 216 assert ( 217 unknown_number_of_arguments( 218 0, 1, a=2, b=3, c=4, 219 ) 220 == () 221 ) 222 223 224# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert None == ()I change my expectation to match reality
216 assert ( 217 unknown_number_of_arguments( 218 0, 1, a=2, b=3, c=4, 219 ) 220 # == () 221 == None 222 ) 223 224 225# Exceptions seenthe test passes because the function can take any number of keyword arguments without knowing how many are in the call.
I add an assertion to see what happens when I call the function with 3 positional arguments
216 assert ( 217 unknown_number_of_arguments( 218 0, 1, a=2, b=3, c=4, 219 ) 220 # == () 221 == None 222 ) 223 224 assert ( 225 unknown_number_of_arguments( 226 0, 1, 2, a=3, b=4, c=5, 227 ) 228 == None 229 ) 230 231 232# 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 arguments200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 # def unknown_number_of_arguments(x, y, **kwargs): 207 def unknown_number_of_arguments(x, y, z, **kwargs): 208 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 assertion calls the function with two positional arguments and it 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 arguments200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 # def unknown_number_of_arguments(x, y, **kwargs): 207 # def unknown_number_of_arguments(x, y, z, **kwargs): 208 def unknown_number_of_arguments(*args, **kwargs): 209 return Nonethe test passes.
*args, **kwargsis Python convention. I change the names to make it clearer200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 # def unknown_number_of_arguments(x, y, **kwargs): 207 # def unknown_number_of_arguments(x, y, z, **kwargs): 208 # def unknown_number_of_arguments(*args, **kwargs): 209 def unknown_number_of_arguments( 210 *positional_arguments, **keyword_arguments 211 ): 212 return None
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?)
200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 # def unknown_number_of_arguments(x, y, **kwargs): 207 # def unknown_number_of_arguments(x, y, z, **kwargs): 208 # def unknown_number_of_arguments(*args, **kwargs): 209 def unknown_number_of_arguments( 210 *positional_arguments, **keyword_arguments 211 ): 212 # return None 213 return positional_arguments, keyword_arguments 214 215 assert ( 216 unknown_number_of_arguments( 217 0, 1, a=2, b=3, 218 ) 219 == None 220 )the 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
unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) -> first_input, last_inputWhen
unknown_number_of_arguments(0, 1, a=2, b=3)runsunknown_number_of_arguments(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 })Using substitution
assert unknown_number_of_arguments(0, 1, a=2, b=3) == None assert None == NoneIf 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 first assertion
215 assert ( 216 unknown_number_of_arguments( 217 0, 1, a=2, b=3, 218 ) 219 # == None 220 == ((0, 1), {'a': 2, 'b': 3}) 221 ) 222 223 assert ( 224 unknown_number_of_arguments( 225 0, 1, a=2, b=3, c=4, 226 ) 227 # == () 228 == None 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 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, c=4)runsunknown_number_of_arguments(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
223 assert ( 224 unknown_number_of_arguments( 225 0, 1, a=2, b=3, c=4, 226 ) 227 # == () 228 # == None 229 == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 230 ) 231 232 assert ( 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 AssertionError
AssertionError: assert ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) == 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, 2, a=3, b=4, c=5)runsunknown_number_of_arguments(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
232 assert ( 233 unknown_number_of_arguments( 234 0, 1, 2, a=3, b=4, c=5, 235 ) 236 # == None 237 == ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 238 ) 239 240 241# Exceptions seenthe test passes.
how Python treats starred expressions
I add variables for the tuple and dictionary of the first assertion
200def test_unknown_number_of_arguments(): 201 # def unknown_number_of_arguments(): 202 # def unknown_number_of_arguments(a): 203 # def unknown_number_of_arguments(**kwargs): 204 # def unknown_number_of_arguments(**kwargs, x): 205 # def unknown_number_of_arguments(x, **kwargs): 206 # def unknown_number_of_arguments(x, y, **kwargs): 207 # def unknown_number_of_arguments(x, y, z, **kwargs): 208 # def unknown_number_of_arguments(*args, **kwargs): 209 def unknown_number_of_arguments( 210 *positional_arguments, **keyword_arguments 211 ): 212 # return None 213 return positional_arguments, keyword_arguments 214 215 a_tuple = (0, 1) 216 a_dictionary = {'a': 2, 'b': 3} 217 assert ( 218 unknown_number_of_arguments( 219 0, 1, a=2, b=3, 220 ) 221 # == None 222 == ((0, 1), {'a': 2, 'b': 3}) 223 )I use the variables to remove repetition of the values
215 a_tuple = (0, 1) 216 a_dictionary = {'a': 2, 'b': 3} 217 assert ( 218 unknown_number_of_arguments( 219 # 0, 1, a=2, b=3, 220 a_tuple, a_dictionary 221 ) 222 # == None 223 # == ((0, 1), {'a': 2, 'b': 3}) 224 == (a_tuple, a_dictionary) 225 )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}When
unknown_number_of_arguments(a_tuple, a_dictionary)runsunknown_number_of_arguments( a_tuple, a_dictionary ) 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 inputs with
*and**so that Python breaks up the contents, allowing them to be used as separate arguments215 a_tuple = (0, 1) 216 a_dictionary = {'a': 2, 'b': 3} 217 assert ( 218 unknown_number_of_arguments( 219 # 0, 1, a=2, b=3, 220 # a_tuple, a_dictionary 221 *a_tuple, **a_dictionary 222 ) 223 # == None 224 # == ((0, 1), {'a': 2, 'b': 3}) 225 == (a_tuple, a_dictionary) 226 ) 227 228 assert ( 229 unknown_number_of_arguments( 230 0, 1, a=2, b=3, c=4, 231 ) 232 # == () 233 # == None 234 == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 235 )the test passes because given
a_tuple = (0, 1) a_dictionary = {'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 variables for the tuple and dictionary of the second assertion
215 a_tuple = (0, 1) 216 a_dictionary = {'a': 2, 'b': 3} 217 assert ( 218 unknown_number_of_arguments( 219 # 0, 1, a=2, b=3, 220 # a_tuple, a_dictionary 221 *a_tuple, **a_dictionary 222 ) 223 # == None 224 # == ((0, 1), {'a': 2, 'b': 3}) 225 == (a_tuple, a_dictionary) 226 ) 227 228 a_tuple = (0, 1) 229 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 230 assert ( 231 unknown_number_of_arguments( 232 0, 1, a=2, b=3, c=4, 233 ) 234 # == () 235 # == None 236 == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 237 )I use the variables to remove repetition of the tuple and dictionary
228 a_tuple = (0, 1) 229 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 230 assert ( 231 unknown_number_of_arguments( 232 # 0, 1, a=2, b=3, c=4, 233 a_tuple, a_dictionary 234 ) 235 # == () 236 # == None 237 # == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 238 == (a_tuple, a_dictionary) 239 )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}When
unknown_number_of_arguments(a_tuple, a_dictionary)runsunknown_number_of_arguments( a_tuple, a_dictionary ) 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 inputs with
*and**so that Python breaks up the contents, allowing them to be used as separate arguments228 a_tuple = (0, 1) 229 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 230 assert ( 231 unknown_number_of_arguments( 232 # 0, 1, a=2, b=3, c=4, 233 # a_tuple, a_dictionary 234 *a_tuple, **a_dictionary 235 ) 236 # == () 237 # == None 238 # == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 239 == (a_tuple, a_dictionary) 240 )the test passes because given
a_tuple = (0, 1) a_dictionary = {'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 last assertion
228 a_tuple = (0, 1) 229 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 230 assert ( 231 unknown_number_of_arguments( 232 # 0, 1, a=2, b=3, c=4, 233 # a_tuple, a_dictionary 234 *a_tuple, **a_dictionary 235 ) 236 # == () 237 # == None 238 # == ((0, 1), {'a': 2, 'b': 3, 'c': 4}) 239 == (a_tuple, a_dictionary) 240 ) 241 242 a_tuple = (0, 1, 2) 243 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 244 assert ( 245 unknown_number_of_arguments( 246 0, 1, 2, a=3, b=4, c=5, 247 ) 248 # == None 249 == ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 250 ) 251 252 253# Exceptions seenI use the variables to remove repetition of the tuple and dictionary from the last assertion
242 a_tuple = (0, 1, 2) 243 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 244 assert ( 245 unknown_number_of_arguments( 246 # 0, 1, 2, a=3, b=4, c=5, 247 a_tuple, a_dictionary 248 ) 249 # == None 250 # == ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 251 == (a_tuple, a_dictionary) 252 ) 253 254 255# 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}When
unknown_number_of_arguments(a_tuple, a_dictionary)runsunknown_number_of_arguments( a_tuple, a_dictionary ) 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 arguments242 a_tuple = (0, 1, 2) 243 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 244 assert ( 245 unknown_number_of_arguments( 246 # 0, 1, 2, a=3, b=4, c=5, 247 # a_tuple, a_dictionary 248 *a_tuple, **a_dictionary 249 ) 250 # == None 251 # == ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 252 == (a_tuple, a_dictionary) 253 ) 254 255 256# Exceptions seenthe test passes because given
a_tuple = (0, 1, 2) a_dictionary = {'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 arguments242 a_tuple = (0, 1, 2) 243 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 244 assert ( 245 unknown_number_of_arguments( 246 # 0, 1, 2, a=3, b=4, c=5, 247 # a_tuple, a_dictionary 248 *a_tuple, **a_dictionary 249 ) 250 # == None 251 # == ((0, 1, 2), {'a': 3, 'b': 4, 'c': 5}) 252 == (a_tuple, a_dictionary) 253 ) 254 255 a_tuple = (0, 1, 2, 'n') 256 assert ( 257 unknown_number_of_arguments(*a_tuple) 258 == () 259 ) 260 261 262# 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
given
a_tuple = (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' )When
unknown_number_of_arguments(a_tuple)runsunknown_number_of_arguments(a_tuple) 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') , {})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
255 a_tuple = (0, 1, 2, 'n') 256 assert ( 257 unknown_number_of_arguments(*a_tuple) 258 # == () 259 == (a_tuple, {}) 260 ) 261 262 263# Exceptions seenthe test passes.
how Python treats double starred expressions
I add another assertion to see what happens when I call the function with ONLY keyword arguments
RED: make it fail
255 a_tuple = (0, 1, 2, 'n')
256 assert (
257 unknown_number_of_arguments(*a_tuple)
258 # == ()
259 == (a_tuple, {})
260 )
261
262 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
263 assert (
264 unknown_number_of_arguments(**a_dictionary)
265 == ()
266 )
267
268
269 # Exceptions seen
the 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 with
an empty tuple since there are no positional arguments
a dictionary of the keyword arguments
given
a_dictionary = {'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' )
When unknown_number_of_arguments(a_dictionary) runs
unknown_number_of_arguments(a_dictionary)
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'})
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).
GREEN: make it pass
I change my expectation to match reality
262 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
263 assert (
264 unknown_number_of_arguments(**a_dictionary)
265 # == ()
266 == ((), a_dictionary)
267 )
268
269
270# Exceptions seen
the test passes.
REFACTOR: make it better
I add one more assertion to see what happens when I call the unknown_number_of_arguments function with no inputs
262 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 263 assert ( 264 unknown_number_of_arguments(**a_dictionary) 265 # == () 266 == ((), a_dictionary) 267 ) 268 269 assert ( 270 unknown_number_of_arguments() 271 == TypeError 272 ) 273 274 275# Exceptions seenthe terminal is my friend, and shows AssertionError
E assert ((), {}) == TypeErrorbecause
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
When
unknown_number_of_arguments()runsunknown_number_of_arguments(a_dictionary) 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 change my expectation to match reality
269 assert ( 270 unknown_number_of_arguments() 271 # == TypeError 272 == ((), {}) 273 ) 274 275 276# Exceptions seenthe test passes.
I remove the commented lines
200def test_unknown_number_of_arguments(): 201 def unknown_number_of_arguments( 202 *positional_arguments, **keyword_arguments 203 ): 204 return positional_arguments, keyword_arguments 205 206 a_tuple = (0, 1) 207 a_dictionary = {'a': 2, 'b': 3} 208 assert ( 209 unknown_number_of_arguments( 210 *a_tuple, **a_dictionary 211 ) 212 == (a_tuple, a_dictionary) 213 )215 a_tuple = (0, 1) 216 a_dictionary = {'a': 2, 'b': 3, 'c': 4} 217 assert ( 218 unknown_number_of_arguments( 219 *a_tuple, **a_dictionary 220 ) 221 == (a_tuple, a_dictionary) 222 )224 a_tuple = (0, 1, 2) 225 a_dictionary = {'a': 3, 'b': 4, 'c': 5} 226 assert ( 227 unknown_number_of_arguments( 228 *a_tuple, **a_dictionary 229 ) 230 == (a_tuple, a_dictionary) 231 )233 a_tuple = (0, 1, 2, 'n') 234 assert ( 235 unknown_number_of_arguments(*a_tuple) 236 == (a_tuple, {}) 237 )239 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 240 assert ( 241 unknown_number_of_arguments(**a_dictionary) 242 == ((), a_dictionary) 243 )245 assert ( 246 unknown_number_of_arguments() 247 == ((), {}) 248 ) 249 250 251# Exceptions seen 252# AssertionError 253# NameError 254# TypeError 255# 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 :ref:`call it<how to call a function with input>`.
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.
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.
s