Functions are one of the most important constructs in computer programming. A function is a single command which, when executed, performs some operations and may return a value. You've already encountered functions in PIC10A, where they may have looked something like this:
// Filename: boldy.cpp
#include <iostream>
int main() {
std::cout << "To boldly go";
return 0;
}
You'll notice the type declaration (int
), the function name (main
), the parameter declaration (()
, i.e. no parameters in this case), and the return value (0
). Python functions have a similar syntax. Instead of a type declaration, one uses the def
keyword to denote function definition. One does not use {}
braces, but one does use a :
colon to initiate the body of the function and whitespace to indent the body.
Since Python is interpreted rather than compiled, functions are ready to use as soon as they are defined.
def boldly_print(): # colon ends declaration and begins definition
print("To boldly go")
# return values are optional
boldly_print()
# ---
To boldly go
Just as in C++, in Python we can pass arguments (or parameters) to functions in order to modify their behavior.
def boldly_print_2(k):
for i in range(k):
print("To boldly go")
boldly_print_2(3)
# ---
To boldly go To boldly go To boldly go
These arguments can be given default values, so that it is not necessary to specify each argument in each function call.
def boldly_print_3(k, verb="go"):
for i in range(k):
print("To boldly " + verb)
boldly_print_3(2)
# ---
To boldly go To boldly go
It is often desirable to use keyword arguments so that your code clearly indicates which argument is being supplied which value:
boldly_print_3(3, "sing") # fine
# ---
To boldly sing To boldly sing To boldly sing
boldly_print_3(k=3, verb="sing") # same as above, easier to read
# ---
To boldly sing To boldly sing To boldly sing
All keyword arguments must be supplied after all positional arguments:
boldly_print_3(k = 3, "sing")
# ---
File "<ipython-input-6-f138e209aff5>", line 1 boldly_print_3(k = 3, "sing") ^ SyntaxError: positional argument follows keyword argument
The global scope is the set of all variables available for usage outside of any function.
x = 3 # available in global scope
x
3
Functions create a local scope. This means:
# variables within the global scope are available within the function
def print_x():
print(x)
print_x()
# ---
3
def print_y():
y = 2
print(y)
print_y()
# ---
2
y
# ---
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-10-9063a9f0e032> in <module> ----> 1 y NameError: name 'y' is not defined
Immutable variables in the global scope cannot be modified by functions, even if you use the same variable name.
def new_x():
x = 7
print(x)
new_x()
# ---
7
print(x)
# ---
3
On the other hand, mutable variables in global scope can be modified by functions. This is usually a bad idea, for reasons we'll discuss in another set of notes.
# this works, but it's a bad idea.
captains = ["Kirk", "Picard", "Janeway", "Sisko"]
def reverse_names():
for i in range(4):
captains[i] = captains[i][::-1]
reverse_names()
captains
['kriK', 'draciP', 'yawenaJ', 'oksiS']
So far, we've seen examples of functions that print but do not return anything. Usually, you will want your function to have one or more return values. These allow the output of a function to be used in future computations.
def boldly_return(k = 1, verb = "go"):
return(["to boldly " + verb for i in range(k)])
x = boldly_return(k = 2, verb = "dance")
x
['to boldly dance', 'to boldly dance']
Your function can return multiple values:
def double_your_number(j):
return(j, 2*j)
x, y = double_your_number(10)
The return
statement immediately terminates the function's local scope, usually returning to global scope. So, for example, a return
statement can be used to terminate a while
loop, similar to a break
statement.
def largest_power_below(a, upper_bound):
i = 1
while True:
i *= a
if a*i >= upper_bound:
return(i)
largest_power_below(3, 10000)
6561