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_data -> process -> output_data
where process is the function_. I think of it like mapping a function f in Mathematics with inputs x and output y
in other words
f(x) -> y
function(input_data) -> output_data
the function does something (the process) with input_data and returns output_data as the result.
how to make a function that takes input
functions_ are made with
the def keyword
a name
parentheses and a colon at the end
the code that makes up the function (its body) comes after the colon
def name_of_function(input_data):
the body of the function
return output_data
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() is '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
101
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 )
123
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 )
133
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
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
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 )
173
174 first_name, blow = 'joe', 'blow'
175 assert (
176 optional_arguments(
177 first_name, blow
178 )
179 == (first_name, blow)
180 )
181
182 first_name = 'john'
183 assert (
184 optional_arguments(
185 first_input=first_name
186 )
187 == (first_name, last_name)
188 )
189
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
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 )
214
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 )
223
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 )
232
233 a_tuple = (0, 1, 2, 'n')
234 assert (
235 unknown_number_of_arguments(*a_tuple)
236 == (a_tuple, {})
237 )
238
239 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'}
240 assert (
241 unknown_number_of_arguments(**a_dictionary)
242 == ((), a_dictionary)
243 )
244
245 assert (
246 unknown_number_of_arguments()
247 == ((), {})
248 )
249
250
251# Exceptions seen
252# AssertionError
253# NameError
254# TypeError
255# 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 the simplest thing it can do is return the input as output, it is called the Identity or Passthrough function and is also in the Truth Table chapter in test_logical_identity.
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() is '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.
how to call a function with input
I can call a function with input by placing an object in parentheses when I call it.
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
I called
identitywhich belongs totest_identity_functionwith one input (None).The function definition (signature) of
identitydoes not allow any inputs when it is called, since the parentheses are empty.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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 == objectwhich raises 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.
I 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 + 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 + 4 == 2 51 assert 1 + 4 == 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. Using substitution since I can treat a call to a function as the object it returnsassert add_x(0) == 2 # inside add_x add_x(number) add_x(0) # add_x returns 2 + number 2 + 0 == 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 == 2I 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
I called
positional_argumentswhich belongs totest_positional_argumentswith one input ('first').The function definition (signature) of
positional_argumentsdoes not allow any inputs when it is called since the parentheses are empty.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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.
I 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
I called
positional_argumentswhich belongs totest_positional_argumentswith two inputs('first'and'last').The function definition (signature) of
positional_argumentsonly allows one input.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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 None. Using substitution since I can treat a call to a function as the object it returnspositional_arguments('first' , 'last' ) positional_arguments(first_input, last_input) return Noneassert positional_arguments('first', '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)
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_input.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. 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('last' , 'first' ) positional_arguments(first_input, last_input) return first_input, last_input return 'last' , 'first'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
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 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_input. Using substitutionpositional_arguments(0 , 1 ) positional_arguments(first_input, last_input) return first_input, last_input return 0 , 1assert 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_input. Using substitutiona_tuple = (0, 1, 2, 'n') a_list = [0, 1, 2, 'n']positional_arguments(a_list , a_tuple ) positional_arguments([0, 1, 2, 'n'], (0, 1, 2, 'n')) return first_input , last_input return [0, 1, 2, 'n'], (0, 1, 2, 'n')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
I called
keyword_argumentswhich belongs totest_keyword_argumentswith 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.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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
I called
keyword_argumentswhich belongs totest_keyword_argumentswith 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.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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 substitutionkeyword_arguments(first_input='first', last_input='last') keyword_arguments(first_input , last_input ) return Noneassert 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'.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 be in 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. Using substitution since I can treat a call to a function as the object it returnskeyword_arguments(last_input='last', first_input='first') keyword_arguments(first_input='first', last_input='last') keyword_arguments(first_input , last_input ) return first_input, last_input return 'first' , 'last'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
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 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. Using substitution since I can treat a call to a function as the object it returnskeyword_arguments(last_input=0, first_input=1) keyword_arguments(first_input=1, last_input=0) keyword_arguments(first_input , last_input ) return first_input, last_input return 1 , 0assert keyword_arguments(last_input=0, first_input=1) == (0, 1) assert (1, 0) == (0, 1)I change my expectation to match reality
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 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
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 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 names. Using substitution since I can treat a call to a function as the object it returnskeyword_arguments( first_input=[0, 1, 2, 'n'], last_input=(0, 1, 2, 'n') ) keyword_arguments(first_input, last_input) return first_input , last_input return [0, 1, 2, 'n'], (0, 1, 2, 'n')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.
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, )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 and I cannot reach it from outside.
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(): 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, )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): 99 return first_input, last_input 100 101 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 ) 123 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 ) 133 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
I can call functions with both positional.
RED: make it fail
I go back to the terminal where the tests are running
I add a test
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 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
I called
args_and_kwargswhich belongs totest_args_and_kwargswith 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.I am violating the function signature when I call it in a way that it was not designed to be called, which raises TypeError.
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 definition for
args_and_kwargstakes one argument (last_input), and the assertion calls the function with two arguments('first', last_input='last'). How does Python know which value to use forlast_inputif I use the position and the keyword?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.The call tells it that the values for
last_inputare 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 substitutionargs_and_kwargs('first' , last_input='last') args_and_kwargs(first_input, last_input ) return Noneassert 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.
I 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' 155 156 assert ( 157 args_and_kwargs( 158 # last_input='last', 'first', 159 'first', last_input='last', 160 ) 161 == ('first', 'last') 162 ) 163 164 165# Exceptions seenI the new variables to remove repetition of
'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' 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.py145def 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 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 the default value for thelast_inputparameter of the function isdoeoptional_arguments('jane') optional_arguments(first_input, last_input='doe')is the same as
optional_arguments('jane' , 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 another assertion to show that I can still 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.
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 nce I do not need to give a value for the
last_inputparameter in the call tosrc.functions.optional_argumentssince the default value for thelast_inputparameter of theoptional_argumentsfunction isdoe. 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 ) 173 174 first_name, blow = 'joe', 'blow' 175 assert ( 176 optional_arguments( 177 first_name, blow 178 ) 179 == (first_name, blow) 180 ) 181 182 first_name = 'john' 183 assert ( 184 optional_arguments( 185 first_input=first_name 186 ) 187 == (first_name, last_name) 188 ) 189 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 expressions 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 == None 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.
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 starred expressions (*).
I use a starred expressions to replace the positional arguments
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 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 reads 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
Using substitution
unknown_number_of_arguments( 0, 1 , a=2, b=3 ) unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) positional_arguments = (0, 1) keyword_arguments = {'a': 2, 'b': 3} return positional_arguments, keyword_arguments return ((0, 1 ), {'a': 2, 'b': 3 })If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (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
Using substitution
unknown_number_of_arguments( 0, 1 , a=2, b=3, c=4 ) 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
Using substitution
unknown_number_of_arguments( 0, 1, 2 , a=3, b=4, c=5 ) 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 reads 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
Using substitution
a_tuple = (0, 1) a_dictionary = {'a': 2, 'b': 3} unknown_number_of_arguments( a_tuple, a_dictionary ) unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) positional_arguments = (a_tuple, a_dictionary) keyword_arguments = {} return positional_arguments, keyword_arguments 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
Using substitution
a_tuple = (0, 1) a_dictionary = {'a': 2, 'b': 3, 'c': 4} unknown_number_of_arguments( a_tuple, a_dictionary ) unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) positional_arguments = (a_tuple, a_dictionary) keyword_arguments = {} return positional_arguments, keyword_arguments 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
Using substitution
a_tuple = (0, 1, 2) a_dictionary = {'a': 3, 'b': 4, 'c': 5} unknown_number_of_arguments( a_tuple, a_dictionary ) unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) positional_arguments = (a_tuple, a_dictionary) keyword_arguments = {} return positional_arguments, keyword_arguments 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' )Using substitution
unknown_number_of_arguments(*a_tuple) unknown_number_of_arguments( *positional_arguments, **keyword_arguments ) positional_arguments = (0, 1, 2, 'n') keyword_arguments = {} return positional_arguments, keyword_arguments 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 reads 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 empty 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' )
Using substitution
unknown_number_of_arguments(**a_dictionary)
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
Using substitution
unknown_number_of_arguments() 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 ) 214 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 ) 223 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 ) 232 233 a_tuple = (0, 1, 2, 'n') 234 assert ( 235 unknown_number_of_arguments(*a_tuple) 236 == (a_tuple, {}) 237 ) 238 239 a_dictionary = {'a': 1, 'b': 2, 'c': 3, 'd': 'n'} 240 assert ( 241 unknown_number_of_arguments(**a_dictionary) 242 == ((), a_dictionary) 243 ) 244 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 use a function to remove repetition.
I can call a function with input by placing an object in parentheses when I call it.
The identity or passthrough function returns its input as output.
I can call a function with positional and keyword arguments.
I can make a function that can take any number of positional or keyword arguments
If I use
*somethingin a function call, it sends the things insomethingas positional arguments.If I use
**somethingin a function call, it sends the key-value pairs ofsomethingas keyword arguments.If I use
*somethingin a function definition, it takes any number of positional arguments as a tuple (anything in parentheses( )separated by a comma).If I use
**somethingin a function definition, it takes any number of keyword arguments as a dictionary (any key-value pairs in curly braces ‘{ }’ separated by commas).
code from the chapter
what is next?
you have covered a bit so far and know
I am going for a walk. Would you like to test using a function to make a string from input?
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.