Chapter 4 - Functions (Python)
Here's a concise walkthrough of the main ideas in Chapter 4, each with a small example.
Defining and calling functions
A function is a mini-program defined with def and run later by calling its name with (). Functions help you avoid duplicated code.
Example:
def hello():
print("Good morning!")
print("Good afternoon!")
print("Good evening!")
hello()
hello()
Arguments and parameters
Parameters are variables in the function definition; arguments are the actual values you pass in when calling.
Example:
def say_hello_to(name): # name is a parameter
print("Good morning,", name)
say_hello_to("Alice") # "Alice" is an argument
say_hello_to("Bob")
After the function returns, name no longer exists outside the function.
Return values and return
A function call evaluates to its return value, which you specify with return. If no return is used, the function returns None.
Example:
def square(x):
return x * x
result = square(5) # 25
print(result)
print(square(3) + square(4)) # 25
Magic 8-ball style function
You can use return plus random to produce different answers depending on a number.
Example:
import random
def get_answer(n):
if n == 1:
return "It is certain"
elif n == 2:
return "Ask again later"
else:
return "Very doubtful"
print(get_answer(random.randint(1, 3)))
The None value
None represents "no value"; it is the single value of type NoneType and is written with a capital N. Functions with no explicit return effectively return None.
Example:
spam = print("Hello!") # prints Hello!
print(spam is None) # True
Also, return by itself returns None:
def do_nothing():
return
print(do_nothing() is None) # True
Named (keyword) parameters in built-ins
Some functions (like print) have named parameters such as end and sep, which you pass as name=value to customize behaviour.
Example (end):
print("Hello", end="")
print("World") # HelloWorld
Example (sep):
print("cats", "dogs", "mice", sep=",")
# cats,dogs,mice
The call stack
Python tracks where to return after each function call using a call stack. Each call adds a frame; returning pops that frame and resumes after the call.
Example:
def c():
print("c() starts")
print("c() returns")
def b():
print("b() starts")
c()
print("b() returns")
def a():
print("a() starts")
b()
print("a() returns")
a()
You'll see the order: a() -> b() -> c() -> back out again.
Local vs global scope
Variables created inside functions live in a local scope; variables created at the top level live in the global scope. Local variables are not visible outside their function.
Example (global can't see local):
def spam():
eggs = "local eggs"
spam()
# print(eggs) # NameError: eggs is not defined
Separate local scopes
Each function call has its own local scope, and different functions' locals cannot see one another's variables.
Example:
def spam():
eggs = "spam local"
bacon()
print("spam sees:", eggs)
def bacon():
eggs = "bacon local"
print("bacon sees:", eggs)
spam()
Here each eggs is separate.
Local scope can read globals
Code inside a function can read global variables (as long as you don't assign to that name locally).
Example:
eggs = "global eggs"
def spam():
print(eggs) # reads global
spam() # prints "global eggs"
print(eggs)
Same name local and global
You can have a global and a local variable with the same name, but it is confusing and usually best avoided.
Example:
eggs = "global"
def spam():
eggs = "spam local"
print(eggs)
def bacon():
eggs = "bacon local"
print(eggs)
spam() # spam local
bacon() # bacon local
print(eggs) # global
The global statement
Use global name inside a function if you want assignments to modify the global variable instead of creating a local one.
Example:
eggs = "global"
def spam():
global eggs
eggs = "spam" # modifies global
spam()
print(eggs) # spam
How to tell if a name is local or global
Rules:
- A variable assigned at the top level is global.
- Inside a function, if there's a
global eggs, theneggsis global there. - Otherwise, if you assign to a variable in a function, it is local.
- If you only read it (no assignment, no
global), it refers to the global.
Example with three functions:
def spam():
global eggs # global
eggs = "spam"
def bacon():
eggs = "bacon" # local
def ham():
print(eggs) # global
eggs = "global"
spam()
print(eggs) # spam
Functions as "black boxes"
You can treat functions as black boxes: know their parameters and return values, but ignore the internal implementation. This makes it easier to use libraries and reason about code.
Example:
def add_tax(amount):
return amount * 1.1
total = add_tax(100) # you don't care how it computes, just that it returns 110
Exceptions and try/except
Errors like dividing by zero raise exceptions that normally crash the program. try/except lets you catch and handle them so the program can continue.
Naive version (crashes):
def spam(divide_by):
return 42 / divide_by
print(spam(2))
print(spam(0)) # ZeroDivisionError
Handled version:
def spam(divide_by):
try:
return 42 / divide_by
except ZeroDivisionError:
print("Error: Invalid argument.")
print(spam(2)) # 21.0
print(spam(0)) # prints error, returns None
try around calls
You can also put the function calls inside the try block; then any exception raised in them will be caught.
Example:
def spam(divide_by):
return 42 / divide_by
try:
print(spam(2))
print(spam(0))
print(spam(1)) # never reached after exception
except ZeroDivisionError:
print("Error: Invalid argument.")
Short program: Zigzag animation
The zigzag program uses functions, an infinite while True loop, time.sleep, and try/except KeyboardInterrupt to animate a row of stars moving back and forth.
import time, sys
indent = 0
indent_increasing = True
try:
while True:
print(" " * indent, end="")
print("********")
time.sleep(0.1)
if indent_increasing:
indent += 1
if indent == 20:
indent_increasing = False
else:
indent -= 1
if indent == 0:
indent_increasing = True
except KeyboardInterrupt:
sys.exit()
Short program: Spike animation
The spike program uses nested for loops, string replication, and time.sleep to draw a spike shape that grows and shrinks repeatedly.
import time, sys
try:
while True:
# Increasing part
for i in range(1, 9):
print("-" * (i * i))
time.sleep(0.1)
# Decreasing part
for i in range(7, 1, -1):
print("-" * (i * i))
time.sleep(0.1)
except KeyboardInterrupt:
sys.exit()
Overall idea of the chapter
The chapter shows how to define reusable functions, understand call stacks and variable scopes, avoid overusing globals, and use try/except to handle errors, culminating in small animated programs that combine all these ideas.