Concepts / Subroutine Parameters and Return Values

Subroutine Parameters and Return Values

A parameterized subroutine declares a parameter list in its definition, which specifies the number and types of values a caller must supply.

  • Programming
Interactive lab

Try it: Defining and Calling Functions

What def and a call really do: def only creates a function object, a call binds the arguments to the parameters in a new frame and runs the body, return ends the call at once and its value replaces the call expression in the caller — while a function that only prints gives back None.

How it works

  1. def name(params): creates a function object and binds the name to it; the body does not run yet.
  2. A call evaluates its arguments, opens a new frame, binds each parameter to its argument and runs the body from the top.
  3. return value ends the call immediately (lines after it never run) and sends the value back; the frame and its local names are discarded.
  4. In the caller, the call expression is replaced by the returned value, which can be stored, used in a larger expression or passed to another call.
  5. A body that ends without return (e.g. one that only prints) makes the call evaluate to None — so x = f() stores None and None + 1 raises TypeError.
  6. Calling a name before its def has run raises NameError: the function does not exist yet.

Default run (18 steps): The program is ready. Python runs it from the top, one statement at a time. Idea: the value a call returns is what the caller stores. … The program finished. Console: 8.

Simplified: Four fixed, hand-written programs; you edit the literal arguments, switch the body between return and print, and move the def after the calls. The lab's own small evaluator runs them (your input is never executed as code); every variant was run in CPython 3.12 and the lab reproduces its sys.settrace line/call/return/exception events, its output and its error message. Functions are shown as <function name> without a memory address.

Educational simulation

Loading the simulation…

A Subroutine That Needs a Starting Number

This article begins with a subroutine you can run rather than with a list of rules. The subroutine prints a 3N+1 sequence. The number the sequence starts from is not written inside the subroutine. Instead, the caller supplies that number each time it calls the subroutine.

Keep one question in mind as you read: when the subroutine runs, where does its starting number come from, and at what point does it first have a value? Answering that question is the whole mechanism this article is about. A subroutine that takes parameters can operate on different inputs, and the way it receives those inputs determines how you read its behavior and how you find the source of a wrong result.

Reading the Parameter List in the Header

A parameterized subroutine declares a parameter list in its definition, and that list specifies the number and types of values a caller must supply. The list is the part of the header inside the parentheses. For the subroutine in this article, the header is shown below.

java
Notes on the code
  • static void: the subroutine's header, naming it print3NSequence
  • int startingValue: the parameter list, declaring exactly one parameter
  • int: the type a caller's value must match
  • startingValue: the parameter name the body uses

Read the parameter list as a contract. It says the subroutine expects exactly one parameter, and that the parameter is of type int. Any caller who wants to use this subroutine must supply one int value. The number and types of the parameters tell any caller what must be supplied, before the caller writes a single line of the call.

Definition

A parameter is a name declared in a subroutine's parameter list. The definition declares the name but does not give it a value. The value arrives from outside, through a call statement.

Two Calls, One Definition

A call statement names the subroutine and supplies a value inside its parentheses. The same definition can be called with different values, and each call produces output based on the value it supplied. The driver below is a small illustration written for this article. It calls the same subroutine twice, once with a literal and once with a variable. The body of the subroutine is not repeated, because it is the same as the definition shown earlier.

java
Notes on the code
  • Line 1 gives the variable K the value 17
  • Line 2 passes the literal 17 as the argument
  • Line 3 passes the current value of K as the argument

Tracing a Call Before the Body Runs

The subroutine definition declares a parameter but does not give it a value. When the computer executes a subroutine call statement, it first assigns the value supplied in the call to the parameter name. Only then does it execute the statements of the subroutine body. Because of this order, the parameter name is ready for use the moment the body begins.

CallerstartingValueSubroutine body1. Assign supplied value2. Body runs and reads startingValue
What happens step by step, from the moment the call executes to the moment the body first sees the parameter's value?

The diagram shows the same path described in words. The caller's value moves to the parameter, and the parameter is then available to the body. The key point is timing. The assignment to the parameter happens before the first statement of the body, which is why the body can rely on the parameter already holding a value.

Why the Body Can Use the Parameter Like a Variable

Inside the body, the parameter name is used in the same way as an ordinary variable name. The body is written in terms of startingValue. It has no direct connection to the name K in the caller. Any variable in the caller could be passed, as long as its current value is what the call should use.

Key point

The name startingValue belongs to the subroutine, and the name K belongs to the caller. The body never sees K. It sees only the value that was assigned to startingValue when the call began.

Finding Where a Wrong Result Starts

When a result is wrong, trace the path in order. First, identify the value the call supplied. Second, check its assignment to the parameter. Third, find the body's first use of the parameter. The first of these three steps where the actual value differs from the expected value is where the problem begins. The diagram below lays out that sequence as a pipeline you can walk through.

Supplied valueliteral or current value ofthe variableAssignment toparameterstartingValue receives thevalueFirst use in bodybody reads startingValuePrinted sequenceoutput from the bodypassed at callbefore body startsbody produces output
At which step in the call-and-execute process does the actual value first differ from the expected value?

Tracing a Surprising Output

Worked example

A sequence that starts from the wrong number

A programmer intends print3NSequence(K) to print the sequence starting at 17, but the output begins at a different number. The call is written as print3NSequence(K), and an earlier line in the program changes K before the call.

  1. 1
    Identify the value the call supplied

    The argument is K, so the value supplied is the current value of K at the moment of the call, not the value K held when it was first declared.

  2. 2
    Check the assignment to the parameter

    The computer assigns that current value to startingValue before the body runs. The assignment copies the value the call supplied, so startingValue matches K at the moment of the call.

  3. 3
    Check the body's first use of the parameter

    The body reads startingValue. Because the parameter already holds the supplied value, the body is working from the value that was current at the call, which is the wrong number here.

  4. 4
    Locate the divergence

    The expected value (17) and the actual value first differ at the supplied-value step. The subroutine's parameter mechanism behaves as described; the input it received was not the value the programmer expected.

Result

The divergence is at the supplied value. The fix is to find the earlier line that changed K, not to look inside the body first.

Mistakes With Parameters

  • Common mistake

    Expecting the definition to supply the parameter's value

    What it looks like

    Reading print3NSequence(int startingValue) and assuming startingValue already holds a number before any call is made.

    Why it fails

    The definition declares the parameter but does not give it a value. The value comes from the call statement.

    Do this instead

    Look at the call statement to find the value. The parameter has no value until the call assigns one.

  • Common mistake

    Assuming the body sees the caller's variable name

    What it looks like

    Expecting the body to refer to K when the call is print3NSequence(K).

    Why it fails

    The body uses the parameter name startingValue. The name K exists only in the caller.

    Do this instead

    Trace the value: K's current value is assigned to startingValue, and the body works with startingValue.

  • Common mistake

    Checking the body first when a result is wrong

    Why it fails

    The body is usually correct for the value it received. The first place the actual value can diverge is the supplied value or its assignment.

  • Common mistake

    Forgetting that the argument is read at the moment of the call

    What it looks like

    Changing K after a call and expecting the earlier output to reflect the new K.

    Why it fails

    The computer takes the current value of K when the call executes.

    Do this instead

    Check what K holds at the exact line of the call, not at some earlier or later point.

Practice Tracing a Call

What do you think happens?

Given int K = 17; followed by print3NSequence(K);, what value does startingValue hold on the first line of the body?

Reveal answer
Answer:

17

The computer takes the current value of K, which is 17, assigns it to startingValue at the moment of the call, and only then executes the body. The body's first line therefore sees startingValue holding 17.

MEDIUM

Suppose the program sets K to 17, calls print3NSequence(K), then sets K to 5, and then calls print3NSequence(17). List the steps in order for the first call, naming the value supplied, the assignment to startingValue, and the body's first use. Then say which value startingValue holds on the second call's first body line, and what K holds at that point.

Hints
  • Identify the exact moment each call's argument is read.
  • For each call, the assignment to startingValue happens before the body's first line.
  • The second call uses a literal, so K's value does not matter for it.

Key Takeaways

  • A parameter list in a subroutine's header tells a caller how many values to supply and of what types. For print3NSequence(int startingValue), the caller must supply exactly one int.
  • The definition does not give the parameter a value. The call statement supplies the value, which is assigned to the parameter before the body executes.
  • A call can supply a literal or a variable. With a variable, the current value of that variable at the moment of the call is what the parameter receives.
  • When a result is wrong, trace the path in order: the supplied value, its assignment to the parameter, and the body's first use of it. The first step where actual differs from expected is where the problem begins.
  • This source material for the lesson describes how parameters receive their values. It does not describe how a return value travels back to the caller, so that part of the title is not covered by this article's source.