functions
what is a function?
A function is a unit or block of code that is callable. This means I can write statements that I can use to do something at a different time from when I write them. They can make code smaller, easier to read, test, reuse, maintain and improve.
Computer Programming involves providing a process with input data and the process returning output data, for example
input_data -> process -> output_data
I think of it like the concept of mapping a function f with inputs x and an output of y
in other words
function(input_data) -> output_data
the function processes input_data and returns output_data as the result.
In Python functions are made with the def keyword, a name, parentheses and a colon at the end
def name_of_function():
preview
Here are the tests I have by the end of the chapter
1import src.functions
2import unittest
3
4
5class TestFunctions(unittest.TestCase):
6
7 def test_making_a_function_w_pass(self):
8 self.assertIsNone(src.functions.w_pass())
9
10 def test_making_a_function_w_return(self):
11 self.assertIsNone(src.functions.w_return())
12
13 def test_making_a_function_w_return_none(self):
14 self.assertIsNone(src.functions.w_return_none())
15
16 def test_constant_function(self):
17 self.assertEqual(
18 src.functions.constant(),
19 'the same thing'
20 )
21
22 def test_identity_function(self):
23 self.assertIsNone(src.functions.identity(None))
24 self.assertEqual(src.functions.identity(object), object)
25
26 def test_functions_w_positional_arguments(self):
27 self.assertEqual(
28 src.functions.w_positional_arguments('first', 'last'),
29 ('first', 'last')
30 )
31 self.assertEqual(
32 src.functions.w_positional_arguments('last', 'first'),
33 ('last', 'first')
34 )
35
36 def test_functions_w_keyword_arguments(self):
37 self.assertEqual(
38 src.functions.w_keyword_arguments(
39 first='first', last='last',
40 ),
41 ('first', 'last')
42 )
43 self.assertEqual(
44 src.functions.w_keyword_arguments(
45 last='last', first='first',
46 ),
47 ('first', 'last')
48 )
49 self.assertEqual(
50 src.functions.w_keyword_arguments('last', 'first'),
51 ('last', 'first')
52 )
53
54 def test_functions_w_positional_and_keyword_arguments(self):
55 self.assertEqual(
56 src.functions.w_positional_and_keyword_arguments(
57 'first', last='last',
58 ),
59 ('first', 'last')
60 )
61
62 def test_functions_w_default_arguments(self):
63 self.assertEqual(
64 src.functions.w_default_arguments('jane'),
65 ('jane', 'doe')
66 )
67 self.assertEqual(
68 src.functions.w_default_arguments('joe', 'blow'),
69 ('joe', 'blow')
70 )
71
72 def test_functions_w_unknown_arguments(self):
73 self.assertEqual(
74 src.functions.w_unknown_arguments(
75 0, 1, 2, 3, a=4, b=5, c=6, d=7,
76 ),
77 ((0, 1, 2, 3), {'a': 4, 'b': 5, 'c': 6, 'd': 7})
78 )
79 self.assertEqual(
80 src.functions.w_unknown_arguments(0, 1, 2, 3),
81 ((0, 1, 2, 3), {})
82 )
83 self.assertEqual(
84 src.functions.w_unknown_arguments(a=4, b=5, c=6, d=7),
85 ((), dict(a=4, b=5, c=6, d=7))
86 )
87 self.assertEqual(
88 src.functions.w_unknown_arguments(),
89 ((), {})
90 )
91
92
93# Exceptions seen
94# AssertionError
95# NameError
96# IndentationError
97# SyntaxError
questions about functions
Here are the questions you can answer after going through this chapter
start the project
I name this project
functionsI open a terminal
then I make a directory for the project
mkdir functionsthe terminal goes back to the command line
.../pumping_pythonI change directory to the project
cd functionsthe terminal shows I am now in the
functionsfolder.../pumping_python/functionsI make a folder for the source code
mkdir srcthe terminal goes back to the command line
.../pumping_python/functionsI use touch to make an empty file for the program in the
srcfoldertouch src/functions.pyon Windows without Windows Subsystem for Linux use
New-Item src/functions.pyinstead oftouch src/functions.pyNew-Item src/functions.pythe terminal goes back to the command line
.../pumping_python/functionsI make a directory for the tests
mkdir teststhe terminal goes back to the command line
I use touch to make an empty file in the
testsfolder to tell Python that it is a Python packageAttention
use 2 underscores (__) before and after
initfor__init__.pynot_init_.pytouch tests/__init__.pyon Windows without Windows Subsystem for Linux use
New-Item tests/__init__.pyinstead oftouch tests/__init__.pyNew-Item tests/__init__.pythe terminal goes back to the command line
I make an empty file for the actual test
touch tests/test_functions.pyon Windows without Windows Subsystem for Linux use
New-Item tests/test_functions.pyinstead oftouch tests/test_functions.pyNew-Item tests/test_functions.pythe terminal goes back to the command line
I open
test_functions.pyin the editor of the Integrated Development Environment (IDE)Tip
I can open a file from the terminal in Visual Studio Code by typing
codeand the name of the file, for example, when I type thiscode tests/test_functions.pytest_functions.pyopens up in the editorI add the first failing test to
test_functions.py1import unittest 2 3 4class TestFunctions(unittest.TestCase): 5 6 def test_failure(self): 7 self.assertFalse(True)I make a virtual environment in the terminal
python3 -m venv .venvon Windows without Windows Subsystem for Linux use
python3 -m venv .venvinstead ofpython3 -m venv .venvpython -m venv .venvthe terminal takes some time then goes back to the command line
I activate the virtual environment
source .venv/bin/activateon Windows without Windows Subsystem for Linux use
.venv/bin/activate.ps1instead ofsource .venv/bin/activate.venv/scripts/activate.ps1the terminal shows
(.venv) .../pumping_python/functionsI upgrade the Python package manager (pip) to the latest version
python3 -m pip install --upgrade pipthe terminal shows pip being uninstalled then installs the latest version or shows that it is already the latest version
I make a
requirements.txtfile for the Python programs my project needsecho "pytest-watch" > requirements.txtthe terminal goes back to the command line
I use pip to use the requirements file to install
pytest-watchpython3 -m pip install --requirement requirements.txton Windows without Windows Subsystem for Linux use
python -m pip install --requirement requirements.txtinstead ofpython3 -m pip install --requirement requirements.txtpython -m pip install --requirement requirements.txtthe terminal shows pip downloads and installs the Python programs that pytest-watch needs to run
I use pytest-watch to run the test
pytest-watchthe terminal shows
================================ FAILURES ================================ _______________________ TestFunctions.test_failure _______________________ self = <tests.test_functions.TestFunctions testMethod=test_failure> def test_failure(self): > self.assertFalse(True) E Functions: True is not false tests/test_functions.py:7: AssertionError ======================== short test summary info ========================= FAILED tests/test_functions.py::TestFunctions::test_failure - Functions: True is not false =========================== 1 failed in X.YZs ============================I hold ctrl (Windows/Linux) or
option or command(MacOS) on the keyboard and use the mouse to click ontests/test_functions.py:7to open it in the editorI add AssertionError to the list of Exceptions seen in
test_functions.py7 self.assertFalse(True) 8 9 10# Exceptions seen 11# AssertionErrorthen I change True to False in the assertion
7 self.assertFalse(False)the test passes
test_making_a_function_w_pass
I can make a function with the pass keyword
RED: make it fail
I change
test_failuretotest_making_a_function_w_pass1import unittest 2 3 4class TestFunctions(unittest.TestCase): 5 6 def test_making_a_function_w_pass(self): 7 self.assertIsNone(src.functions.w_pass()) 8 9 10# Exceptions seenNameError: name 'src' is not definedI add it to the list of Exceptions seen in
test_functions.py10# Exceptions seen 11# AssertionError 12# NameError
GREEN: make it pass
I add an import statement at the top of the file
1import src.functions 2import unittest 3 4 5class TestFunctions(unittest.TestCase):the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_pass'functions.pyin thesrcfolder does not have anything namedw_passinside itI add to the list of Exceptions seen
11# Exceptions seen 12# AssertionError 13# ModuleNotFoundError 14# AttributeErrorI open
functions.pyfrom thesrcfolder in the editor, then I add a function definition1def w_pass(): 2 passthe test passes
the test checks if the result of the call to
src.functions.w_passis Nonethe function definition simply says pass and the test passes
pass is a placeholder keyword which allows the function definition to follow Python language rules
the test passes because all functions return None by default, as if the function has an invisible line that says
return None, which leads me to the next test
test_making_a_function_w_return
I can make a function with a return statement
RED: make it fail
I add a new failing test in test_functions.py
7 def test_making_a_function_w_pass(self):
8 self.assertIsNone(src.functions.w_pass())
9
10 def test_making_a_function_w_return(self):
11 self.assertIsNone(src.functions.w_return())
12
13
14# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_return'
functions.py in the src folder does not have w_return in it
GREEN: make it pass
I add a new function with pass to functions.py
1def w_pass():
2 pass
3
4
5def w_return():
6 pass
the test passes
REFACTOR: make it better
I change pass to a return statement
5def w_return():
6 return
the test is still green.
I have 2 functions with different statements in their body but they both return None, because “all functions return None by default, as if the function has an invisible line that says return None”, which leads me to the next test
test_making_a_function_w_return_none
I can make a function with a return statement that says what the function returns
RED: make it fail
I add another failing test to test_functions.py
10 def test_making_a_function_w_return(self):
11 self.assertIsNone(src.functions.w_return())
12
13 def test_making_a_function_w_return_none(self):
14 self.assertIsNone(src.functions.w_return_none())
15
16
17# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_return_none'
w_return_none is not defined in functions.py in the src folder
GREEN: make it pass
I add a function definition to functions.py
5def w_return():
6 return
7
8
9def w_return_none():
10 return
the test passes
REFACTOR: make it better
I add None to the return statement
9def w_return_none():
10 return None
I like to write my functions this way, saying exactly what it returns, that way anyone can tell what the function returns without knowing what it does or even understanding Python code
test_constant_function
constant functions always return the same thing when they are called
RED: make it fail
I add a test to test_functions.py
13 def test_making_a_function_w_return_none(self):
14 self.assertIsNone(src.functions.w_return_none())
15
16 def test_constant_function(self):
17 self.assertEqual(
18 src.functions.constant(),
19 'the same thing'
20 )
21
22
23# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'constant'
I have not added a definition for constant in functions.py in the src folder
GREEN: make it pass
I add the function to functions.py
9def w_return_none():
10 return None
11
12
13def constant():
14 return None
the terminal shows AssertionError
AssertionError: None != 'the same thing'
what the constant function returns and what the test expects are different. I change the return statement to make them match
13def constant():
14 return 'the same thing'
the test passes.
A constant function always return the same thing when called, I can use them in place of variables, though the number of cases where they are faster than variables is pretty small. It is something like if the function is called less than 10 times, but who’s counting?
test_identity_function
The identity function returns its input as output, it’s also in the Truth Table chapter in test_logical_identity
RED: make it fail
I add a failing test in test_functions.py
16 def test_constant_function(self):
17 self.assertEqual(
18 src.functions.constant(),
19 'the same thing'
20 )
21
22 def test_identity_function(self):
23 self.assertIsNone(src.functions.identity(None))
24
25
26# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'identity'
GREEN: make it pass
I add a function to
functions.py13def constant(): 14 return 'the same thing' 15 16 17def identity(): 18 return NoneTypeError: identity() takes 0 positional arguments but 1 was giventhe definition for
identitydoes not allow inputs and the test sends None as inputI add the error to the list of Exceptions seen in
test_functions.py26# Exceptions seen 27# AssertionError 28# NameError 29# AttributeError 30# TypeErrorI add a name in parentheses for the
identityfunction to take input infunctions.py17def identity(the_input): 18 return Nonethe test passes. I am genius
REFACTOR: make it better
The requirement for the identity function is that it returns the same thing it is given, the test is currently passing when None is given as input. Does it pass when another value is given or does it always return None? Time to write a test
I add a new assertion to
test_identity_functionintest_functions.py22def test_identity_function(self): 23 self.assertIsNone(src.functions.identity(None)) 24 self.assertEqual(src.functions.identity(object), object)the terminal shows AssertionError
AssertionError: None != <class 'object'>the function returns None instead of
<class 'object'>in the second case, I am not all the way genius, yetI change the return statement of
identityinfunctions.pyto match the expectation17def identity(the_input): 18 return the_inputthe test passes
I sometimes use the Identity Function when I am testing connections to see if my test is connected to what I am testing. If I can send input and received the same input back then I can start making changes to see what results I get.
The Identity Function takes one input, the following tests are for functions that take more than one.
test_functions_w_positional_arguments
RED: make it fail
I add a failing test to test_functions.py
22 def test_identity_function(self):
23 self.assertIsNone(src.functions.identity(None))
24 self.assertEqual(src.functions.identity(object), object)
25
26 def test_functions_w_positional_arguments(self):
27 self.assertEqual(
28 src.functions.w_positional_arguments('first', 'last'),
29 ('first', 'last')
30 )
31
32
33# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_positional_arguments'
GREEN: make it pass
I add a function to
functions.py17def identity(the_input): 18 return the_input 19 20 21def w_positional_arguments(): 22 return NoneTypeError: w_positional_arguments() takes 0 positional arguments but 2 were givenI make the function take input by adding a name in parentheses
21def w_positional_arguments(first_input): 22 return NoneTypeError: w_positional_arguments() takes 1 positional argument but 2 were givenI make
w_positional_argumentstake another input by adding another name in parentheses21def w_positional_arguments(first_input, last_input): 22 return Nonethe terminal shows AssertionError
AssertionError: None != ('first', 'last')I change the return statement
21def w_positional_arguments(first_input, last_input): 22 return first_input, last_inputthe test passes
REFACTOR: make it better
The problem with giving arguments this way is that they have to be in the order the function expects or I get a different behavior. I add a test to
test_functions.pyto show this26 def test_functions_w_positional_arguments(self): 27 self.assertEqual( 28 src.functions.w_positional_arguments('first', 'last'), 29 ('first', 'last') 30 ) 31 self.assertEqual( 32 src.functions.w_positional_arguments('last', 'first'), 33 ('first', 'last') 34 ) 35 36 37# Exceptions seenthe terminal shows AssertionError
AssertionError: Tuples differ: ('last', 'first') != ('first', 'last')I change the expectation of the test in
test_functions.py31 self.assertEqual( 32 src.functions.w_positional_arguments('last', 'first'), 33 ('last', 'first') 34 )the test passes.
The order matters when passing positional arguments to a function, because they are processed based on the position or in the order they are given to the function
test_functions_w_keyword_arguments
There is a problem with using positional arguments, the inputs must always be supplied in the right order, which means the function behaves in an unexpected way when it gets input out of order.
I can use Keyword Arguments to make sure it behaves how I want even when I send input out of order
RED: make it fail
I add a new test to test_functions.py
31 self.assertEqual(
32 src.functions.w_positional_arguments('last', 'first'),
33 ('last', 'first')
34 )
35
36 def test_functions_w_keyword_arguments(self):
37 self.assertEqual(
38 src.functions.w_keyword_arguments(
39 first_input='first', last_input='last',
40 ),
41 ('first', 'last')
42 )
43
44
45# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_keyword_arguments'
functions.py in the src folder is missing a definition for w_keyword_arguments
GREEN: make it pass
I add a function definition to
functions.py21def w_positional_arguments(first_input, last_input): 22 return first_input, last_input 23 24 25def w_keyword_arguments(): 26 return NoneTypeError: w_keyword_arguments() got an unexpected keyword argument 'first_input'I add the name for the argument in parentheses
25def w_keyword_arguments(first_input): 26 return NoneTypeError: w_keyword_arguments() got an unexpected keyword argument 'last_input'. Did you meanI add the name in parentheses
25def w_keyword_arguments(first_input, last_input): 26 return Nonethe terminal shows AssertionError
AssertionError: None != ('first', 'last')I change the return statement
25def w_keyword_arguments(first_input, last_input): 26 return first_input, last_inputthe test passes
w_keyword_arguments and w_positional_arguments are the same functions. The only difference in the definitions is their names. The difference that matters in the tests is in how I call the functions.
In the first case I use positional arguments which have to be given in order
w_positional_arguments('first', 'last')
w_positional_arguments('last', 'first')
in the second case I use keyword arguments which use the names of the variables in parentheses in the function definition when calling the it
w_keyword_arguments(first_input='first', last_input='last')
REFACTOR: make it better
I add another test with the keyword arguments given out of order in
test_functions.py36 def test_functions_w_keyword_arguments(self): 37 self.assertEqual( 38 src.functions.w_keyword_arguments( 39 first_input='first', last_input='last', 40 ), 41 ('first', 'last') 42 ) 43 self.assertEqual( 44 src.functions.w_keyword_arguments( 45 last_input='last', first_input='first', 46 ), 47 ('last', 'first') 48 )the terminal shows AssertionError
AssertionError: Tuples differ: ('first', 'last') != ('last', 'first')the order stayed the same
I change the expectation to match
43 self.assertEqual( 44 src.functions.w_keyword_arguments( 45 last_input='last', first_input='first', 46 ), 47 ('first', 'last') 48 )the test passes. Keyword Arguments allow the input to be passed in any order
I can still call the function without using the names, the same way I did in test_functions_w_positional_arguments. I add an assertion to show this
43 self.assertEqual( 44 src.functions.w_keyword_arguments( 45 last_input='last', first_input='first', 46 ), 47 ('first', 'last') 48 ) 49 self.assertEqual( 50 src.functions.w_keyword_arguments('last', 'first'), 51 ('first', 'last') 52 )the terminal shows AssertionError
AssertionError: Tuples differ: ('last', 'first') != ('first', 'last')I change the expectation to match
49 self.assertEqual( 50 src.functions.w_keyword_arguments('last', 'first'), 51 ('last', 'first') 52 ) 53 54 55# Exceptions seenthe test passes
Positional Arguments MUST be given in the expected order, Keyword Arguments can be given in any order
test_functions_w_positional_and_keyword_arguments
I can write functions that take both positional and keyword arguments<test_functions_w_keyword_arguments>
RED: make it fail
I add a failing test to test_functions.py
49 self.assertEqual(
50 src.functions.w_keyword_arguments('last', 'first'),
51 ('last', 'first')
52 )
53
54 def test_functions_w_positional_and_keyword_arguments(self):
55 self.assertEqual(
56 src.functions.w_positional_and_keyword_arguments(
57 last_input='last', 'first',
58 ),
59 ('first', 'last')
60 )
61
62
63# Exceptions seen
the terminal shows SyntaxError
SyntaxError: positional argument follows keyword argument
I cannot put a keyword argument before a positional argument in Python
GREEN: make it pass
I change the order of the arguments to follow Python rules
54 def test_functions_w_positional_and_keyword_arguments(self): 55 self.assertEqual( 56 src.functions.w_positional_and_keyword_arguments( 57 'first', last_input='last', 58 ), 59 ('first', 'last') 60 )the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_positional_and_keyword_arguments'I add a function to
functions.py25 def w_keyword_arguments(first_input, last_input): 26 return first_input, last_input 27 28 29 def w_positional_and_keyword_arguments(): 30 return NoneTypeError: w_positional_and_keyword_arguments() got an unexpected keyword argument 'last_input'I add the name to the function definition in parentheses in
functions.py29def w_positional_and_keyword_arguments(last_input): 30 return Nonethe terminal shows
TypeError: w_positional_and_keyword_arguments() got multiple values for argument 'last_input'I add another name in parentheses
29def w_positional_and_keyword_arguments(last_input, first_input): 30 return NoneTypeError: w_positional_and_keyword_arguments() got multiple values for argument 'last_input'Python cannot tell the difference between the 2 values since
last_inputis both the second positional argument and passed in as a keyword argumentI change the order of the names in parentheses
29def w_positional_and_keyword_arguments(first_input, last_input): 30 return Nonethe terminal shows AssertionError
AssertionError: None != ('first', 'last')I cannot put positional arguments after keyword arguments
I change the return statement
29def w_positional_and_keyword_arguments(first_input, last_input): 30 return first_input, last_inputthe test passes.
There is no difference between the last 3 functions except their names, they all have this pattern
def a_name(first_input, last_input):
return first_input, last_input
what is different is the way I called them in the tests
w_positional_arguments('first', 'last')
w_positional_arguments('first', 'last')
w_keyword_arguments(first_input='first', last_input='last')
w_keyword_arguments(last_input='last', first_input='first')
w_keyword_arguments('last', 'first')
w_positional_and_keyword_arguments('first', last_input='last')
test_functions_w_default_arguments
I can use positional and keyword arguments when I want a function to take inputs that are needed and inputs that are NOT needed
RED: make it fail
I add a failing test to test_functions.py
54 def test_functions_w_positional_and_keyword_arguments(self):
55 self.assertEqual(
56 src.functions.w_positional_and_keyword_arguments(
57 'first', last_input='last',
58 ),
59 ('first', 'last')
60 )
61
62 def test_functions_w_default_arguments(self):
63 self.assertEqual(
64 src.functions.w_default_arguments('jane', last_name='doe'),
65 ('jane', 'doe')
66 )
67
68
69# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_default_arguments'. Did you mean: 'w_keyword_arguments'?
GREEN: make it pass
I add a function to functions.py
29def w_positional_and_keyword_arguments(first_input, last_input):
30 return first_input, last_input
31
32
33def w_default_arguments(first_name, last_name):
34 return first_name, last_name
the test passes
REFACTOR: make it better
I remove
last_name='doe'from the call tow_default_argumentsintest_functions.py62 def test_functions_w_default_arguments(self): 63 self.assertEqual( 64 src.functions.w_default_arguments('jane'), 65 ('jane', 'doe') 66 )TypeError: w_default_arguments() missing 1 required positional argument: 'last_name'the
last_nameargument is needed when the function is calledI make the argument a choice by giving it a default value in
functions.py33def w_default_arguments(first_name, last_name='doe'): 34 return first_name, last_namethe test passes.
Calling the function without the
last_nameargumentw_default_arguments('jane')is now the same as calling it with the default value
w_default_arguments('jane', last_name='doe')I add another assertion to
test_functions.pyto show that I can still call the function with different values62 def test_functions_w_default_arguments(self): 63 self.assertEqual( 64 src.functions.w_default_arguments('jane'), 65 ('jane', 'doe') 66 ) 67 self.assertEqual( 68 src.functions.w_default_arguments('joe', 'blow'), 69 () 70 ) 71 72 73# Exceptions seenthe terminal shows AssertionError
AssertionError: Tuples differ: ('joe', 'blow') != ()I change the expectation to match
67 self.assertEqual( 68 src.functions.w_default_arguments('joe', 'blow'), 69 ('joe', 'blow') 70 )the test passes
test_functions_w_unknown_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 are sent to the function when it is called
RED: make it fail
I add a new test to test_functions.py
67 self.assertEqual(
68 src.functions.w_default_arguments('joe', 'blow'),
69 ('joe', 'blow')
70 )
71
72 def test_functions_w_unknown_arguments(self):
73 self.assertEqual(
74 src.functions.w_unknown_arguments(
75 0, 1, 2, 3, a=4, b=5, c=6, d=7,
76 ),
77 None
78 )
79
80
81# Exceptions seen
the terminal shows AttributeError
AttributeError: module 'src.functions' has no attribute 'w_unknown_arguments'. Did you mean: 'w_keyword_arguments'?
GREEN: make it pass
I add a function to
functions.py33def w_default_arguments(first_name, last_name='doe'): 34 return first_name, last_name 35 36 37def w_unknown_arguments(): 38 return NoneTypeError: w_unknown_arguments() got an unexpected keyword argument 'a'I add the name to the function definition
37def w_unknown_arguments(a): 38 return NoneTypeError: w_unknown_arguments() got multiple values for argument 'a'I had this same problem in test_functions_w_positional_and_keyword_arguments, Python cannot tell which arguments are positional or keyword arguments yet
Python has a way to allow passing any number of keyword arguments without knowing how many they are. I use it to replace
ain the parentheses37def w_unknown_arguments(**kwargs): 38 return NoneTypeError: w_unknown_arguments() takes 0 positional arguments but 4 were givenI add a name for the first positional argument
37def w_unknown_arguments(**kwargs, x): 38 return Nonethe terminal shows SyntaxError
SyntaxError: arguments cannot follow var-keyword argumenta reminder that I cannot put positional arguments after keyword arguments
I add SyntaxError to the list of Exceptions seen in
test_functions.py81# Exceptions seen 82# AssertionError 83# NameError 84# AttributeError 85# TypeError 86# SyntaxErrorI change the order of the inputs in
w_unknown_argumentsinfunctions.py37def w_unknown_arguments(x, **kwargs): 38 return NoneTypeError: w_unknown_arguments() takes 1 positional argument but 4 were givenI can add names for the other positional arguments, or I can do something like what I did with the keyword arguments
37def w_unknown_arguments(*args, **kwargs): 38 return Nonethe test passes
REFACTOR: make it better
*args, **kwargsis Python convention. I change the names to be clearer37def w_unknown_arguments(*arguments, **keyword_arguments): 38 return Nonethe test is still green
I want the function to return its input, remember the identity function, I change the return statement
37def w_unknown_arguments(*arguments, **keyword_arguments): 38 return arguments, keyword_argumentsthe terminal shows
AssertionError: ((0, 1, 2, 3), {'a': 4, 'b': 5, 'c': 6, 'd': 7}) != NoneI copy the tuple from the terminal and use it to change the expectation in
test_functions_w_unknown_argumentsintest_functions.py72 def test_functions_w_unknown_arguments(self): 73 self.assertEqual( 74 src.functions.w_unknown_arguments( 75 0, 1, 2, 3, a=4, b=5, c=6, d=7, 76 ), 77 ((0, 1, 2, 3), {'a': 4, 'b': 5, 'c': 6, 'd': 7}) 78 )the test passes
how Python reads positional arguments
I want to see what happens when I call the function with ONLY positional arguments. I add a new assertion
73 self.assertEqual(
74 src.functions.w_unknown_arguments(
75 0, 1, 2, 3, a=4, b=5, c=6, d=7,
76 ),
77 ((0, 1, 2, 3), {'a': 4, 'b': 5, 'c': 6, 'd': 7})
78 )
79 self.assertEqual(
80 src.functions.w_unknown_arguments(0, 1, 2, 3),
81 ()
82 )
the terminal shows AssertionError
AssertionError: Tuples differ: ((0, 1, 2, 3), {}) != ()
I change the expectation to match
79 self.assertEqual(
80 src.functions.w_unknown_arguments(0, 1, 2, 3),
81 ((0, 1, 2, 3), {})
82 )
83
84
85# Exceptions seen
the test passes. The function reads the positional arguments as a tuple (things in parentheses (()) separated by commas)
how Python reads keyword arguments
I add another assertion to see what happens when I call the function with ONLY keyword arguments
79 self.assertEqual(
80 src.functions.w_unknown_arguments(0, 1, 2, 3),
81 ((0, 1, 2, 3), {})
82 )
83 self.assertEqual(
84 src.functions.w_unknown_arguments(a=4, b=5, c=6, d=7),
85 ()
86 )
the terminal shows
AssertionError: Tuples differ: ((), {'a': 4, 'b': 5, 'c': 6, 'd': 7}) != ()
I change the expectation to match
83 self.assertEqual(
84 src.functions.w_unknown_arguments(a=4, b=5, c=6, d=7),
85 ((), dict(a=4, b=5, c=6, d=7))
86 )
87
88
89# Exceptions seen
the test passes. The function reads the keyword arguments as a dictionary (key-value pairs in curly braces ({}) separated by commas)
I add one more assertion to see what happens when I call the function with no inputs
83 self.assertEqual( 84 src.functions.w_unknown_arguments(a=4, b=5, c=6, d=7), 85 ((), dict(a=4, b=5, c=6, d=7)) 86 ) 87 self.assertEqual( 88 src.functions.w_unknown_arguments(), 89 () 90 )the terminal shows
AssertionError: Tuples differ: ((), {}) != ()I change the expectation to match
87 self.assertEqual( 88 src.functions.w_unknown_arguments(), 89 ((), {}) 90 ) 91 92 93# Exceptions seenthe test passes
The function reads
positional arguments as tuples and
This is why the update method of dictionaries can take a dictionary as input
close the project
I close the file(s) I have open in the editor(s)
I click in the terminal and exit the tests with ctrl+c on the keyboard
I deactivate the virtual environment
deactivatethe terminal goes back to the command line,
(.venv)is no longer on the left side.../pumping_python/functionsI 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 with
the def keyword
as a reminder
positional arguments must come before keyword arguments
I can use
**kwargswhen I do not know how many keyword arguments the function has to takekeyword arguments are represented as dictionaries
I can use
*argswhen I do not know how many positional arguments the function has to takepositional arguments are represented as tuples
the identity function returns its input
constant functions always return the same thing
How many questions can you answer after going through this chapter?
code from the chapter
what is next?
you have covered a bit so far and know
Would you like to test how to pass values from tests to functions with assert methods?