Defining Functions with Parameters
Defining a function creates a variable that holds a function object with type 'function'.
A Function Name Holds a Value
A function definition does more than give a block of instructions a name. It creates a variable whose value is a function object. That function object has type function, so the function name can be inspected like other values.
The first print inspects the function object. The second print reports a type of function.Calling the Function
Defining a function stores the instructions, but it does not execute the function body at that moment. To execute it, write the function name followed by parentheses. This is the same calling syntax used for built-in functions such as print(). The parentheses tell Python to execute the instructions held by the function object.
def show_message():
print("First line")
print("Second line")
print("Before call")
show_message()
print("After call")- 1Python reaches
print("Before call")and displays the first line. - 2Python reaches
show_message()and pauses at that call site. - 3Control moves into
show_message, where the two print statements run in order. - 4When the function finishes, control returns to the line immediately after the call.
- 5Python displays the final line.
- The definition creates the function object.
- The first print runs before the call.
- The call executes both statements in the function body in order.
- The final print runs after the function finishes.
Before call
First line
Second line
After callNested Calls and Reuse
A function can call another function. In that situation, control first moves from the original caller into the outer function. The outer function then calls the inner function. After the inner function finishes, control returns to the outer function, and after the outer function finishes, control returns to the original caller.
The body of repeat_lyrics reuses print_lyrics twice instead of duplicating the two print statements. This illustrates how larger behavior can be built from simpler functions.
What do you think happens?
When repeat_lyrics() runs, which function's body begins executing first after the call to repeat_lyrics?
Reveal answer
The body of repeat_lyrics begins first. Its first statement then calls print_lyrics, so control moves into print_lyrics before returning to repeat_lyrics.
A call enters the function named at the call site. Nested calls add another movement into the inner function and then return control outward.
List Parameters Share the Original List
When a list is passed to a function, the function receives a reference to the original list, not a copy. A parameter can therefore refer to the same list object that the caller holds. If the function performs an in-place operation on that list, the caller observes the change after the function returns.
def add_item(items):
items.append("new")
values = ["old"]
add_item(values)
print(values)- 1values is assigned a list containing one item.
- 2values is passed to
add_item. - 3Inside
add_item, items refers to the original list. - 4
items.append("new")changes that existing list. - 5After the call, printing values shows both items.
- values refers to the caller's list.
- The parameter items refers to that original list.
- append modifies the existing list in place.
- The caller sees the modified list after the function returns.
["old", "new"]What do you think happens?
After this call, what does values contain: add_item(values), where add_item executes items.append("new")?
Reveal answer
["old", "new"]
The parameter refers to the original list, and append modifies that list in place.
Mutation Versus New Lists
| Operation | Effect | What the caller observes |
|---|---|---|
| append | Modifies the existing list in place | The caller's list changes |
| del | Modifies the existing list in place | The caller's list changes |
| + | Creates a new list | The original list is preserved unless reassigned |
| Slicing | Creates a new list | The original list is preserved unless reassigned |
List operations differ according to whether they change the existing object or create another list.
def add_in_place(items):
items.append("new")
values = ["old"]
add_in_place(values)values becomes ["old", "new"] because append modifies the original list.
def with_extra_item(items):
return items + ["new"]
values = ["old"]
updated = with_extra_item(values)values remains ["old"], while updated refers to a new list containing ["old", "new"].
The first function changes the list received from its caller by using del. The second uses slicing, which creates a new list, and returns that new list. The caller can preserve the original while storing the returned result separately.
Mistakes with List Parameters
Common mistake Assuming that passing a list automatically passes a copy.
What it looks likedef add_item(items): items.append("new")Why it failsA list argument gives the function a reference to the original list, so an in-place operation affects the caller's list.
Do this insteadTreat append and del as changes to the shared original list. Use + or slicing and return the result when the original should be preserved.
Common mistake Treating append and + as equivalent.
What it looks likeitems.append("new") versus items + ["new"]Why it failsappend modifies the existing list, while + creates a new list.
Do this insteadDecide whether the function should mutate the caller's list or return a separate list before choosing the operation.
Common mistake Expecting a function body to run when the function is defined.
What it looks likedef show_message(): print("Hello")Why it failsThe definition creates the function object; the body runs when the function is called.
Do this insteadUse show_message() to execute the function body.
Common mistake Forgetting the parentheses when calling a function.
What it looks likeshow_message instead of show_message()Why it failsThe name by itself refers to the function object. Parentheses are the call syntax that tells Python to execute it.
Do this insteadWrite the function name followed by parentheses when you want to call it.
Practice the Execution Trace
Predict the final contents of numbers and doubled before reading the hints.
def add_marker(items):
items.append("marker")def make_doubled(items):
return items + itemsnumbers = ["start"]
add_marker(numbers)
doubled = make_doubled(numbers)Hints
- Track whether
add_markeruses an in-place operation or creates a new list. - Track which list
make_doubledreturns and where that returned list is stored. - The original variable numbers and the returned variable doubled do not have to refer to the same list.
Tracing Two List Functions
Determine the values of numbers and doubled after the practice code runs.
- 1Start
numbers refers to a list containing ["start"].
- 2First call
add_markerreceives a reference to the original list. append modifies that list in place, so numbers becomes ["start", "marker"]. - 3Second call
make_doubleduses +. That operation creates a new list containing the contents of numbers twice. - 4Assignment
The returned new list is assigned to doubled, while numbers continues to refer to the original modified list.
numbers is ["start", "marker"]. doubled is ["start", "marker", "start", "marker"].
Key Takeaways
- 1Defining a function creates a variable holding a function object whose type is function.
- 2A custom function is called with its name followed by parentheses, just like a built-in function.
- 3A call moves control into the function body, and completion returns control to the caller.
- 4A list parameter refers to the original list, so append and del can change the caller's list.
- 5Use in-place operations when changing the original is intentional; use + or slicing and return a new list when preserving the original is important.
At a glance
- A function definition creates a variable that holds a function object.
- Calling a function transfers control into its body and then back to the caller.
- Passing a list gives the function a reference to the original list rather than a copy.
- append and del modify a list in place, while + and slicing create new lists.
- Choose mutation or a returned new list according to whether the caller should observe changes to the original.