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.
show_messagevariablefunction objectinstructionsfunctiontypeholdshas type
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.

Code walkthrough
def show_message():
    print("First line")
    print("Second line")

print("Before call")
show_message()
print("After call")
  1. 1
    Python reaches print("Before call") and displays the first line.
  2. 2
    Python reaches show_message() and pauses at that call site.
  3. 3
    Control moves into show_message, where the two print statements run in order.
  4. 4
    When the function finishes, control returns to the line immediately after the call.
  5. 5
    Python 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.
Output
Before call
First line
Second line
After call
callerbefore callshow_messagefunction bodycallerafter callcallreturn control
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.

Code walkthrough
def add_item(items):
    items.append("new")

values = ["old"]
add_item(values)
print(values)
  1. 1
    values is assigned a list containing one item.
  2. 2
    values is passed to add_item.
  3. 3
    Inside add_item, items refers to the original list.
  4. 4
    items.append("new") changes that existing list.
  5. 5
    After 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.
Output
["old", "new"]
valuescaller variableoriginal list["old"]itemsfunction parameterrefers torefers to
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

List operations differ according to whether they change the existing object or create another list.

Before
def add_in_place(items):
    items.append("new")

values = ["old"]
add_in_place(values)

values becomes ["old", "new"] because append modifies the original list.

After
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"].

values["old"]original list["old"]values["old"]new list["old", "new"]updated["old", "new"]refers tooriginal preservedrefers to
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

  • Common mistake

    Assuming that passing a list automatically passes a copy.

    What it looks like
    def add_item(items):
        items.append("new")
    Why it fails

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

    Do this instead

    Treat 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 like
    items.append("new") versus items + ["new"]
    Why it fails

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

    Do this instead

    Decide 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 like
    def show_message():
        print("Hello")
    Why it fails

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

    Do this instead

    Use show_message() to execute the function body.

  • Common mistake

    Forgetting the parentheses when calling a function.

    What it looks like
    show_message instead of show_message()
    Why it fails

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

    Do this instead

    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.
Worked example

Tracing Two List Functions

Determine the values of numbers and doubled after the practice code runs.

  1. 1
    Start

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

  2. 2
    First call

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

  3. 3
    Second call

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

  4. 4
    Assignment

    The returned new list is assigned to doubled, while numbers continues to refer to the original modified list.

Result

numbers is ["start", "marker"]. doubled is ["start", "marker", "start", "marker"].

Key Takeaways

  1. 1
    Defining a function creates a variable holding a function object whose type is function.
  2. 2
    A custom function is called with its name followed by parentheses, just like a built-in function.
  3. 3
    A call moves control into the function body, and completion returns control to the caller.
  4. 4
    A list parameter refers to the original list, so append and del can change the caller's list.
  5. 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.

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.

Related concepts

More concepts starting with D