10. Functions, I: Read help docs and use¶
There is a huge amount of functionality in the Python universe. We want to understand how each item is intended to be used, to know what options exist, and hopefully to find example cases that demonstrate appropriate syntax. This is true for both built-in functions and for those in the zoo of modules that we can import. Fortunately, most objects in Python have a help description or docstring (= "documentation string"). And later, when we write our own functions, we will want to write our own help content, too.
There are multiple syntaxes for checking the help of items, some
depending on the module being explored. We will demonstrate some
using both built-in functions and those from the math
module. So,
let's start by importing the latter so we have it available:
import math
When reading help files, we will focus a lot on understanding functions and functionality associated with objects.
10.1. Thinking about functions: input(s) and output(s)¶
When looking to use a function, we definitely want to know what the output(s) will be, which is what the function returns. There can be zero, one or more returned items. For example, sometimes a function will just print a message and no value is output; or we might calculate the area of a rectangle and return that; other times, we might output a set of values like distance, velocity and acceleration. Occasionally, we might just change one of the inputs instead of returning something else (called operating in-place).
The inputs to a function in computing are typically called arguments (and often abbreviated as args) or parameters. As with outputs, there can be zero, one or more arguments when using (or calling) a function. But inputs have an added layer of consideration because functions can have a combination of required and optional arguments. The optional arguments have default values (i.e., a parameter is set to have a particular value unless we specify otherwise), and there are a few categories of optional arguments, which have different syntaxes.
We can have a bit of intuition about this variety and flexibility of input usage from the mathematical context. Consider the example of the Gaussian function in mathematics, which is often written as:
(1)¶
While this is a function of one variable , to calculate an explicit value we also need to know the values of the parameters and . To reflect this fact, sometimes in mathematics the LHS is written as , which balances displaying the "full" number of inputs for a calculation with differentiating the kinds of inputs (the actual variable from the parameters and ) with a semi-colon ";". Python will not use a semi-colon to separate inputs, but it is a useful concept to keep in mind.
Furthermore, in many applications (such as statistics) one particular case of the Gaussian is overwhelmingly used. It is called the Normal distribution, which occurs when the parameters and . So, while recognizing that someone talking about a Gaussian has the freedom to choose any values for and that they want, we might picture a Gaussian as having those Normal parameter values, unless otherwise specified (i.e., by default).
We will find that many functions in Python work in an analogous way: some argument(s) might be required, and some might be optional (while having default values). We will now investigate how all of this is specified in the way help files are presented.
10.2. Help doc examples¶
Let's look at some help files starting with math module's cosine function. One way to view its help is to put a question mark
?
after it:math.cos?
... which produces:
1Signature: math.cos(x, /) 2Docstring: Return the cosine of x (measured in radians). 3Type: builtin_function_or_method
Line 1 shows the possible arguments (the signature of the function), which are separated by commas. In this case, there is actually just one input, represented by
x
, and the/
that looks like an odd second argument is actually a relatively new Python syntax that shows the "end of required arguments"; its usefulness would be more apparent if there were more kinds of arguments present, and it is not included when executing the function. In Line 2, we see the docstring and learn that the output (what is returned by the function) is the evaluation of , and importantly that the units ofx
are radians (they could also easily have been degrees, so this clarification is vital). Line 3 tells us the type of the object we are looking at---we mentioned before that all objects in Python have a type, and even functions like this do!Thus, we can probably guess what the value of:
math.cos(3.14159)
should be close to. We now also know that if we entered
math.cos(180)
hoping for the input to be interpreted as degrees, then we would become sad---or worse, we might not realize our mistaken understanding/usage of the function, and then we would have incorrect results lurking in our code. And if we tried to calculate the cosine of 3 separate values here withmath.cos(1, 2, 3)
, then we should not be surprised to see an error:1--------------------------------------------------------------------------- 2TypeError Traceback (most recent call last) 3<ipython-input-103-b0fec430d18e> in <module> 4----> 1 math.cos(1, 2, 3) 5 6TypeError: cos() takes exactly one argument (3 given)
... politely reminding us in Line 6 about the correct number of arguments, as opposed to what we entered (recall: arguments are always separated by commas).
Let's look at another function in math:
math.pow?
What does this do?
1Signature: math.pow(x, y, /) 2Docstring: Return x**y (x to the power of y). 3Type: builtin_function_or_method
In Line 1, we see that this requires 2 arguments to be used (again, the / symbol is not an input and just helps classify the args). In Line 2, we see what the output is (what is returned), and how each of x and y are used.
When we try to use this function and provide two values, such as with
math.pow(3, 4)
, how does Python know which value to provide to which argument? Well, the required arguments are here are called position-only arguments, meaning that our input values will just be mapped in the order they appear: we need to input 2 values, and the first will be given tox
and the second toy
. There is no alternative here, such as writing writingmath.pow(y=4, x=3)
: that would produce an error. (Note that the single argument formath.cos
, above, is also a position-only arg.)You can check that
math.pow(3, 4)
andmath.pow(4, 3)
provide the expected results for and , respectively.Let's look at the help of Python's built-in print() function:
print?
... which produces:
1Docstring: 2print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) 3 4Prints the values to a stream, or to sys.stdout by default. 5Optional keyword arguments: 6file: a file-like object (stream); defaults to the current sys.stdout. 7sep: string inserted between values, default a space. 8end: string appended after the last value, default a newline. 9flush: whether to forcibly flush the stream. 10Type: builtin_function_or_method
Line 2 shows some new syntax in the argument list. Firstly, there is an input
value
, and then the ellipsis...
: recall from our earlier examples of using print(), that we can enter any number of comma-separated items to be displayed in the sequence they are entered. So, that part of the syntax means that we can have any number of position-only arguments as inputs.What about the new syntax with the
=
for the other arguments, such assep=' '
? Well, these are called keyword arguments, often abbreviated as the fun-to-say kwargs. They have default values (sep
's is an empty space str,' '
), and the use of each is described in the help as well (sep
's is provided in Line 7). To alter the values of these parameters, we have to explicitly identify the kwarg by name when using the function.As an example, see the difference between the two lines output by:
print('apple', 'banana', 'ice cream') print('apple', 'banana', 'ice cream', sep='---')
... which are:
apple banana ice cream apple---banana---ice cream
(As a sidenote, we can now understand why there were spaces appearing in our output lists of printed items: it was a parameter default in the function.)
Note that in order to specify new values for these kwargs, we must use the keyword explicitly; their usage is not position-based. Thus, note that trying to reproduce the second print line with:
print('apple', 'banana', 'ice cream', '---')
would not be successful. It would not produce an Error in this specific case, but the interpreter would just treat the last argument as another "value" item to display in sequence:
apple banana ice cream ---
Since there can be any number input values to display in sequence, it makes sense that the other arguments would require a keyword to identify them specifically.
However, below we will see examples where arguments can be both keyword- and position-specified.
Note
We might notice that the
/
symbol is not present in the argument list here. Well, sometimes it is and sometimes it isn't, across all possible Python functions. C'est la vie, we guess. It's OK by us to not have it, and we should mostly rely on recognizing position-only args and kwargs without it.How about if we wanted to find out about calculating logarithms? Asking for the help:
math.log?
... produces:
1Docstring: 2log(x, [base=math.e]) 3Return the logarithm of x to the given base. 4 5If the base not specified, returns the natural logarithm (base e) of x. 6Type: builtin_function_or_method
In the function usage in Line 2, we see some interesting syntax. We should now recognize that the first argument is a position-only arg and one that is required. The second one looks like a keyword argument, but why does it have square brackets around it? In fact, the syntax signifies that the second argument is optional and has a default value... but the square-brackets surrounding it highlight that it's not a normal keyword argument can be specified as
base=VALUE
. Instead, it would be specified only by position. So we could usemath.log(100, 10)
, but notmath.log(100, base=10)
.The reason for having a second argument at all relates to the mathematical Gaussian example above. When asking for the log of a number, we actually need more information: namely, what base the calculation is using (recall: if , then , where b is the base). So, the base is effectively an extra parameter we need to know, but there are very common bases that people often use, such as e (for natural logarithms) and 10. The syntax of this help file and Line 5 tells us that
base
's default value here is the numbere
(Euler's number, approx. 2.7182818), but we could specify another base if we wanted.Thus,
math.log(100)
evaluates to4.605170185988092
, while:math.log(100, 10)
evaluates to
2.0
.Comparing this function's argument syntax with that of the
print()
function, we can see why this function's kwarg could also be specified by position here, but was not possible in the former. Consider thatmath.log()
takes a single, required argument, and at most one more argument: so, whatever is specified second (if at all) could only be thebase
parameter. Theprint()
function can both take any number of initial values and there are several additional parameters that can be entered: there is no way to distinguish what parameter is being re-defined (if any) purely by position.As a final quick example, we note that we could check the help for non-functional things, too, such as the
math.e
we came across above:math.e?
... produces:
1Type: float 2String form: 2.718281828459045 3Docstring: Convert a string or number to a floating point number, if possible.
This object is type float, and indeed does appear to be the Euler number. Note that we would not use this features as
math.e()
, because it is not a function, but instead more like a variable name within the modulemath.e
.
Q: Look at the help information on the built-in function
round()
. How many arguments does it take, which are required,
and which are optional? Let num = 1234.56789
, and how could we
use round to display num
rounded to the hundredths digit? How
about to the hundreds digit (read the help file carefully for this,
and maybe test different optional values...)
Q: Is the output type in each of the uses of round()
in the
previous question surprising, or expected? Why?
10.3. Other ways to check documentation/help¶
The ?
-based syntax for seeing help documentation is convenient
(likely math.cos?
, above). There is also a built-in function in
Python called help()
that can be useful. In many cases, it
provides nearly the same documentation information as using ?
, but
occasionally it is different. Additionally, if one is using
IPython
, the help()
provides a scrollable version of the help
doc (sometimes different than what is shown with the ?
) for
modules and functions, within which one can perform string searches.
For example, after typing:
help(math)
1Help on module math:
2
3NAME
4 math
5
6MODULE REFERENCE
7 https://docs.python.org/3.7/library/math
8
9 The following documentation is automatically generated from the Python
10 source files. It may be incomplete, incorrect or include features that
11 are considered implementation detail and may vary between Python
12 implementations. When in doubt, consult the module reference at the
13 location listed above.
14
15DESCRIPTION
16 This module provides access to the mathematical functions
17 defined by the C standard.
18
19FUNCTIONS
20 acos(x, /)
21 Return the arc cosine (measured in radians) of x.
22
23 acosh(x, /)
24 Return the inverse hyperbolic cosine of x.
25
26 asin(x, /)
27 Return the arc sine (measured in radians) of x.
28
29 asinh(x, /)
30 Return the inverse hyperbolic sine of x.
31
32...
which continues on.
However, in many cases, there isn't much difference between using
help()
and ?
. The output of help(math.log)
is:
1Help on built-in function log in module math:
2
3log(...)
4 log(x, [base=math.e])
5 Return the logarithm of x to the given base.
6
7 If the base not specified, returns the natural logarithm (base e) of x.
... which can be compared to math.log?
, above.
Finally, sometimes help()
will show unexpected documentation. For
example, the output of help(math.e)
will display information about
float objects in general, not about the specific object and its value.
It isn't totally random, because we did see above that math.e
is
itself a float, but it still might not be what we prefer. So, you can
always see whether using help()
or ?
provides the expected
results.
For contents of the numpy module specifically, there is a function to
display help doc strings. It is np.info()
, and its usage is, for
example:
np.info(np.log)
It also accepts non-numpy objects as inputs, e.g., np.info(print)
and np.info(math.log)
.
10.4. Keyboard shortcuts (esp. for IPython)¶
In a Jupyter-notebook, one can just interact with the documentation using webpage features.
But in some other environments, like Ipython
or the plain
python
one, one might want (or need) to navigate with keyboard
presses, to view, search and even exit the help documentation. We
briefly list some keyboard features for interacting with help files in
these environments.
up/down arrow keys (or mouse scroll wheel): scroll up or down the help page
Spacebar: scroll down a whole page
/
: prepare to start a search for text within the whole help page (the prompt will change from:
to/
)then type the string to search for (for example:
log
)then hit Enter to start a "downward" search. If instances exist within/below the current view panel, each will be highlighted and the first will be jumped to.
n
: navigate to the next (downward) found item
q
: typing this at the:
prompt will quit viewing the current documentation.
10.5. Practice¶
What is the difference between an arg and a kwarg?
How many values must the user input when using each of the following functions? How many keyword-arguments are in each?
math.atan() math.atan2() math.log() np.log() random.random() random.gauss() os.chdir()
How many different -type functions does Python's
math
module contain? How is each different?Read the help doc of the NumPy module's
zeros()
function. How do you make an array of 5 zeros, where each is of datatype int?Import the random module using
import random
. Use therandint()
function in that module to generate a random int in the range[-5, 5)
. Execute it several times to verify that the range seems appropriate.