Concepts / Defining Functions with Parameters

Defining Functions with Parameters

Defining a function creates a variable that holds a function object with type 'function'.

  • Programming

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.

python
Output
The first print inspects the function object. The second print reports a type of function.
holdshas typeshow_messagevariablefunction objectinstructionsfunctiontype
What value does the function name hold after the definition runs?

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")

callreturn controlcallerbefore callshow_messagefunction bodycallerafter call
How does control move from the caller into the function and back after the function finishes?

Nested 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.

python

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?

  • The body of repeat_lyrics
  • The body of print_lyrics
  • Neither function body
Reveal answer

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)

refers torefers tovaluescaller variableoriginal list["old"]itemsfunction parameter
What does the function parameter refer to, and how is that reference connected to the caller's list?

What do you think happens?

After this call, what does values contain: add_item(values), where add_item executes items.append("new")?

  • ["old"]
  • ["old", "new"]
  • A separate list whose contents cannot be predicted
Reveal answer

Answer: ["old", "new"]

The parameter refers to the original list, and append modifies that list in place.

Mutation Versus New Lists

OperationEffectWhat the caller observes
appendModifies the existing list in placeThe caller's list changes
delModifies the existing list in placeThe caller's list changes
+Creates a new listThe original list is preserved unless reassigned
SlicingCreates a new listThe original list is preserved unless reassigned
refers tooriginal preservedrefers tovalues["old"]original list["old"]values["old"]new list["old", "new"]updated["old", "new"]
What changes when a function mutates the original list compared with when it creates and returns a separate list?
python

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

  • Assuming that passing a list automatically passes a copy.

    A list argument gives the function a reference to the original list, so an in-place operation affects the caller's list.

    Fix: Treat append and del as changes to the shared original list. Use + or slicing and return the result when the original should be preserved.

  • Treating append and + as equivalent.

    append modifies the existing list, while + creates a new list.

    Fix: Decide whether the function should mutate the caller's list or return a separate list before choosing the operation.

  • Expecting a function body to run when the function is defined.

    The definition creates the function object; the body runs when the function is called.

    Fix: Use show_message() to execute the function body.

  • Forgetting the parentheses when calling a function.

    The name by itself refers to the function object. Parentheses are the call syntax that tells Python to execute it.

    Fix: Write the function name followed by parentheses when you want to call it.

Practice the Execution Trace

MEDIUM

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 + items numbers = ["start"] add_marker(numbers) doubled = make_doubled(numbers)

Hints
  • Track whether add_marker uses an in-place operation or creates a new list.
  • Track which list make_doubled returns 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.

Start: numbers refers to a list containing ["start"].

First call: add_marker receives a reference to the original list. append modifies that list in place, so numbers becomes ["start", "marker"].

Second call: make_doubled uses +. That operation creates a new list containing the contents of numbers twice.

Assignment: 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

  1. Defining a function creates a variable holding a function object whose type is function.
  2. A custom function is called with its name followed by parentheses, just like a built-in function.
  3. A call moves control into the function body, and completion returns control to the caller.
  4. A list parameter refers to the original list, so append and del can change the caller's list.
  5. Use in-place operations when changing the original is intentional; use + or slicing and return a new list when preserving the original is important.

Key Takeaways

  • 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.