PYTHON / FUNCTIONS
Defining and calling functions
Define functions with def, call them with parentheses, and tell the difference between a function object and the result of running it.
What you will learn
- Write a def block with a colon and a consistently indented body
- Call with name() and know that a bare name only evaluates the object
- Explain why a module-level call above the def raises NameError
- Pass functions around by name and call them through an alias
Understanding Defining and calling functions
`def` is not a declaration the interpreter collects ahead of time; it is an ordinary statement that runs when control reaches it. When it runs, Python compiles the indented body into a code object, wraps that in a function object, and binds the function object to the name after `def` in whatever namespace the statement executed in. Nothing inside the body runs at that moment, which is why a body full of nonsense arithmetic will still define cleanly and only blow up later when you call it.
Running the body is the job of the call operator, the pair of parentheses. Writing `countdown` looks up the name and hands you the function object; writing `countdown()` looks up the same object and then invokes it, which creates a fresh execution frame, runs the statements top to bottom, and produces a value for the call expression. A body that never executes a `return` still produces `None`, so `result = countdown()` stores `None` rather than storing the function.
Because `def` executes in order, a module-level call written above the `def` fails with `NameError`: the name is not bound yet. Inside another function's body the name is only looked up when that function is called, so helper functions may be defined lower in the file. The same rule explains redefinition and aliasing: a second `def` with the same name just rebinds the name to a new object, while `alias = countdown` gives a second name for the original object, and that object stays reachable through the alias no matter what the first name later points to.
def countdown():
print(3)
print(2)
print(1)
print(type(countdown))
print(countdown.__name__)
countdown # evaluates the function object and throws it away: nothing runs
countdown() # the parentheses run the body
result = countdown()
print(result)A def statement builds a function object and binds it to a name; the parentheses of a call, not the name itself, are what run the body.
Worked examples
def runs at runtime
Shows that a name is only bound once the def statement has actually executed, while a call inside another body is resolved later.
def report():
try:
helper()
except NameError:
print("helper does not exist yet")
report()
def helper():
print("helper is defined now")
report()Example explained
Line 1`def report():` binds the name `report` but does not check that `helper` exists.
Line 2The first `report()` call looks up `helper` at that instant and fails, because the second def has not run yet.
Line 3The `def helper():` statement executes and binds the name at module level.
Line 4The second `report()` call repeats the same lookup, now finds the object, and calls it.
Functions stored and called through other names
Demonstrates that a function object can be put in a list or bound to a second name and called from there.
def add_tax():
print("adding tax")
def apply_discount():
print("applying discount")
pipeline = [add_tax, apply_discount] # no parentheses: the objects go in the list
for step in pipeline:
step() # parentheses here: each one runs
alias = add_tax
print(alias is add_tax)
alias()Example explained
Line 1`[add_tax, apply_discount]` builds a list of two function objects; writing `add_tax()` there would store the return values instead.
Line 2`step()` calls whichever object the loop variable currently names.
Line 3`alias = add_tax` binds a second name to the same object, so `is` reports True.
Line 4`alias()` runs the identical body, because a call depends on the object, not on the name used to reach it.
Important notes
A def cannot have an empty body; use `pass` as the single indented statement if you want a placeholder that defines cleanly.
A second `def` with the same name silently replaces the first binding, so a duplicated function name in one file is not an error, just a lost function.
Common mistakes
Writing `countdown` instead of `countdown()`: Python raises no error at all, the object is evaluated and discarded, and the body never runs, which looks like the function is broken.
Calling a function on a line above its `def` at module level: the name is still unbound, so the script dies with `NameError: name 'countdown' is not defined`.
Overwriting the function with its own result, as in `countdown = countdown()`: the name now holds `None`, and the next `countdown()` fails with `TypeError: 'NoneType' object is not callable`.
Try it yourself
Change, predict, then run
Define `banner()` so it prints three lines of dashes and a title, call it twice, then bind it to `b`, print `b is banner`, and call it once through `b`.
Open the Python workspaceCheck your understanding
What does this script print? def f(): print("A") g = f def f(): print("B") g()
- A
- B
- A then B
- NameError, because f was redefined
Show answer
`g = f` copies a reference to the function object that existed at that moment, so `g` still names the body printing "A". Answering "B" assumes `g` tracks the name `f`, but the second def only rebinds `f` to a brand-new object and leaves other references to the old one untouched.