Python Part I · Module 11 · Functions
Turning a Repeated Sequence Into a Function
Chapter 11.1 ended with a function and deliberately refused to explain it. This chapter explains it. A definition has four parts and each one does a specific job, and the part people most often misread is not the syntax at all — it is what actually happens at the moment Python reads a def. Getting that right turns a whole category of confusing bugs into obvious ones, because it tells you where to look when a function misbehaves: almost never at the definition, and almost always at the call.
Foundation15 min readPythonPart IFundamentalsFunctionsdef
Part I · Module 11 · Chapter 11.2 · Turning a Repeated Sequence Into a Function
1. One Definition
Here is the rule from Chapter 11.1, written once:
def count_errors(log):
errors = 0
for line in log:
if "UVM_ERROR" in line or "UVM_FATAL" in line:
errors += 1
return errors
print(count_errors(["UVM_ERROR @1: a", "UVM_INFO @2: b", "UVM_FATAL @3: c"]))2Five lines define it; one line uses it. Everything in this chapter is about those five.
(As in Chapters 10.2 and 10.7, log is a list of lines standing in for a file you would normally read. Opening files is Chapter 15.2; nothing about the function changes when the real thing replaces the list.)
2. The Four Parts
def count_errors(log): ← the header
errors = 0 ┐
for line in log: │
if "UVM_ERROR" in line: │ the body
errors += 1 │
return errors ┘def— the keyword that begins a definition.count_errors— the name. An ordinary name, following Chapter 2.4's rules, chosen so the call site reads as a sentence.(log)— the parameter list. These are names the body may use, which will be supplied when the function is called.:and the indented block — the body, the work the function does.
The colon and the indented block are the same construct as Chapter 9.2's. A def line ends in : and a block follows, exactly as an if line does — nothing new to learn about where the body begins and ends.
3. The Name Is an Ordinary Name
def count_errors(...) binds the name count_errors to a function, in the same way x = 5 binds x to an integer. That is not an analogy; it is literally what happens:
def ping():
return "pong"
print(type(ping))
print(ping())<class 'function'>
pongping holds a value whose type is function. The consequences of that are Chapter 11.3's subject — for now it is enough that a definition creates a name, which is why Section 6's ordering rule works the way it does.
4. A Definition Does Not Run the Body
This is the fact worth the chapter.
When Python reads a def, it does not execute the indented block. It packages the block up, binds it to the name, and moves on. The body runs only when the function is called.
That is testable in the way Module 9 and Module 10 have tested every other claim about what does not happen:
def broken():
print(1 / 0)
print("the definition was read, and nothing raised")the definition was read, and nothing raised1 / 0 is sitting in that program. Python read the whole definition, bound the name, and carried on.
Call it and the same line raises immediately:
def broken():
print(1 / 0)
print("about to call")
broken()about to call
ZeroDivisionError: division by zeroOne consequence worth stating: a syntax error inside a body is still caught before anything runs, because the file must parse in full. Only running is deferred, not reading.
5. The Body Is an Ordinary Block
Anything that can appear in a block can appear in a function body — conditionals, loops, accumulators, nested loops, everything from Modules 9 and 10:
def summarise(log):
errors = 0
fatals = 0
for line in log:
if "UVM_FATAL" in line:
fatals += 1
elif "UVM_ERROR" in line:
errors += 1
print("errors:", errors, "fatals:", fatals)
return errors + fatals
total = summarise(["UVM_ERROR @1: a", "UVM_FATAL @2: b", "UVM_INFO @3: c"])
print("total:", total)errors: 1 fatals: 1
total: 2Note the elif — a fatal is counted once, not as both. That is Chapter 9.1's branch-order rule doing its job inside a function body, with no new rules.
Names created in the body — errors, fatals, line — belong to the call. Chapter 11.10 covers exactly what that means; for now, notice that nothing outside the function can see them.
6. Define Before You Call
def is a statement. Like every other statement, it runs when execution reaches it, top to bottom. So a call placed above the definition fails:
print(count_errors(["UVM_ERROR @1: a"]))
def count_errors(log):
return 0NameError: name 'count_errors' is not definedAt the moment line 1 ran, the def had not executed, so the name did not exist. This is Chapter 1.11's NameError, from a cause that now has a precise explanation.
Move the definition above the call and it works:
def count_errors(log):
return 0
print(count_errors(["UVM_ERROR @1: a"]))0One thing that is allowed: a function body may call a function defined later in the file, because the body is not executed at definition time. By the time the call actually runs, both definitions have been read. That is a direct consequence of Section 4.
7. Parameters and Arguments Are Different Words
The distinction matters from here on, so it is worth fixing precisely:
- A parameter is a name in the definition's header. It is a placeholder.
- An argument is the value supplied at the call.
def count_errors(log): # `log` is a PARAMETER
return len(log)
messages = ["a", "b", "c"]
print(count_errors(messages)) # `messages` is an ARGUMENT3Inside the body, the parameter name log refers to whatever the caller passed. The caller's name for it — messages here — is irrelevant inside the function, and the function never learns it.
| Where it appears | What it is | |
|---|---|---|
| parameter | in the def header | a name the body may use |
| argument | at the call site | the value supplied for it |
A function with two parameters needs two arguments, separated by commas:
def count_marker(log, marker):
found = 0
for line in log:
if marker in line:
found += 1
return found
lines = ["UVM_ERROR @1: a", "UVM_WARNING @2: b", "UVM_ERROR @3: c"]
print(count_marker(lines, "UVM_ERROR"))
print(count_marker(lines, "UVM_WARNING"))2
1That second parameter is what turns a function that counts one thing into a function that counts whatever you ask for — and it is Chapter 11.4's subject.
8. A Function With No Parameters
The parentheses are required even when there is nothing to put in them:
def banner():
return "=" * 20
print(banner())
print(banner())====================
====================Empty parentheses in the header, empty parentheses at the call. A function with no parameters takes no arguments, and Chapter 11.3 shows what happens when you give it one anyway.
9. Predict, Run, Observe
# A
def twice(text):
return text + text
print("A:", twice("ab"))
# B
def noisy():
print("B: body ran")
print("B: defined")
noisy()
# C
def outer():
return inner()
def inner():
return "C: inner reached"
print(outer())
# D
def count(items):
n = 0
for item in items:
n += 1
return n
print("D:", count([1, 2, 3]), count([]))The verified output:
A: abab
B: defined
B: body ran
C: inner reached
D: 3 0A — the body runs at the call and its result comes back.
B — B: defined prints first. Reading the def produced no output at all; the body ran only when noisy() was called.
C — outer refers to inner, which is defined below it. That is fine, because the body is not executed until outer() is called, by which point both defs have run. Section 6's rule is about execution order, not text order.
D — one definition, two calls, two answers.
10. Exercises
Exercise 1 — label the parts. For def classify(status, strict):, name the keyword, the function name and each parameter. Then write a call to it and name each argument.
Exercise 2 — define before calling. Write a two-line script that calls a function on line 1 and defines it on line 3. Record the exact error. Then fix it by moving one block, and say which rule you applied.
Exercise 3 — the body does not run. Write a function whose body would raise if executed, and a program that defines it and prints a message without calling it. Confirm nothing raises, then add the call and record what changes.
Exercise 4 — two parameters. Write count_marker(log, marker) from Section 7 and use it to count UVM_INFO, UVM_ERROR and UVM_FATAL in the same log with three calls.
Exercise 5 — extract it. Take any loop you wrote in Module 10 that produces one value and turn it into a function with one parameter. Call it twice with different data.
Exercise 6 — no parameters. Write a function that takes nothing and returns a fixed separator line, and use it between two printed sections. Say in one sentence why it still needs parentheses.
Exercise 7 — forward reference. Write two functions where the first calls the second, with the second defined afterwards. Explain in two sentences why this works and why Exercise 2's version did not.
Exercise 8 — name it. For each, give a function name of three words or fewer: (a) returns True when a log has no errors (b) returns the first failing test name (c) turns a list of lines into a list of severities (d) returns the longest runtime in a results list.
11. Summary
- A definition has four parts: the
defkeyword, the name, the parameter list in parentheses, and the indented body. - The body is an ordinary block — the same colon-and-indent rule as Chapter 9.2, and anything from Modules 9 and 10 can go in it.
defbinds a name to a function value, exactly as=binds a name to any other value.- A definition does not run the body.
1 / 0inside a function raises nothing until the function is called. - So a misbehaving function is broken at the call or in the body, not at the
defline — and the traceback names both. - Syntax errors are still caught before anything runs, because the file must parse in full. Only execution is deferred.
defis a statement that runs in order, so calling a function above its definition is aNameError.- A body may call a function defined later, because the body executes after the whole file has been read.
- A parameter is a name in the header; an argument is a value at the call. The function never learns the caller's name for it.
- Put definitions at the top and the work at the bottom.
The next chapter looks at the other half: what actually happens when a function is called.
Where this fits
Part of the Python curriculum.
