Static Member Variables in Classes
A subroutine can use local variables declared inside it, formal parameters that receive values from the caller, and variables defined outside it that remain accessible to other parts of the program.
Try it: Scope: Local, Enclosing, Global, Built-in
How Python looks up a name Local → Enclosing → Global → Built-in, why assigning to a name inside a function makes it local (and raises UnboundLocalError if it is read first), and what global, nonlocal and closures change.
How it works
- Before a function runs, Python marks every name assigned inside it as local to that function, unless it is declared global or nonlocal.
- Reading a name searches the local namespace, then enclosing functions, then the module's globals, then the built-ins; the first hit wins.
- A local name read before it has a value raises UnboundLocalError (Python does not fall back to the global).
- global x makes assignments go to the module namespace; nonlocal x makes them go to the nearest enclosing function.
- A function's local frame disappears when it returns, unless an inner function (a closure) still uses its variables.
Default run (10 steps): Ready: Local shadows global. Every name is looked up Local, then Enclosing, then Global, then Built-in. … Finished. Printed: inside: 99 / outside: 10.
Simplified: The lab's own small Python interpreter runs a fixed, hand-written script (you choose the variant and the values, never the code); nothing is executed as real Python. Every script was run in CPython 3.12 with the same inputs and the lab reproduces its output and error messages. Built-ins shows only the names the script used.
Loading the simulation…
Predicting What a Subroutine Changes
Once you start writing subroutines, a practical question comes up quickly: when code inside a subroutine works with a name, whose name is it, and who else is allowed to see it? Many beginners assume that every name inside a subroutine is connected to the rest of the program in the same way. That assumption leads to predictable surprises. This article sorts out which names a subroutine can reach, which of those names can influence the caller, and why the answer depends on where a name was originally declared.
What do you think happens?
A caller has a whole-number variable holding the value 5 and passes it to a subroutine. Inside the subroutine, the parameter is increased by 10. Once the subroutine returns, what does the caller's variable hold?
Reveal answer
5
For a primitive type such as a whole number, the parameter receives a copy of the caller's value. Changing the parameter inside the subroutine changes only that copy, so the caller's original variable keeps its value of 5.
Three Kinds of Names Inside a Subroutine
A subroutine can work with three different kinds of variables. The first is a local variable, which the subroutine declares for its own use. The second is a formal parameter, which is the name in the subroutine's header that receives a value from whoever calls it. The third is a variable declared outside the subroutine, which the subroutine can still reach. These three categories differ in where their names come from and in how far their effects travel.
The diagram below shows the main sources of names a subroutine can use. Parameters and locals are both created for the subroutine, while the outer variables already existed before the subroutine ran. That single difference, whether a name belongs to the subroutine or to the wider program, explains most of the behavior discussed in the rest of this article.
Local Variables and Their Isolation
A local variable is declared inside a subroutine and has no connection to the outside world. Its entire job is to support the internal workings of that subroutine. Because of this, a local variable never affects the rest of the program. The subroutine can create it, change it many times, and discard it, and nothing outside the subroutine notices.
This isolation is the main reason programmers prefer local variables for temporary calculations. The subroutine can use them freely without creating side effects that other code must keep track of. When a learner later asks when a local variable comes into existence and disappears during a call, the answer is tied to the subroutine's own execution: the local exists for the work of that subroutine and is not something the caller can rely on afterward.
Formal Parameters and the Copy Rule
A formal parameter is the mechanism for getting information into a subroutine. When the subroutine is called, the caller's values are dropped into the parameter names, and the subroutine works with those names from then on. Once execution of the subroutine begins, a parameter behaves much like a local variable. The parameter name is a fresh name inside the subroutine, even though it started with a value supplied by the caller.
The consequence for primitive types is direct. A change made to the parameter inside the subroutine has no effect on the rest of the program. The caller's variable keeps whatever value it had before the call. The diagram below compares the caller's variable and the parameter before and after the subroutine changes the parameter.
Tracing the caller's variable through the call
The caller has count = 5 and passes count to a subroutine whose parameter is n. Inside, n = n + 10 runs. What does count hold after the call returns?
- 1Start of the call
The caller has count holding 5. The value is dropped into the parameter n, which now also holds 5.
- 2Inside the subroutine
The line n = n + 10 makes n hold 15. The name count is a different name and is not touched by this line.
- 3Return to the caller
The subroutine finishes and its parameter name is no longer in use. The caller's count still holds the value it had before the call.
count holds 5 after the call. The change to n never reached the caller.
5- The function's parameter n receives a copy of the value 5.
- n = n + 10 changes only the local name n inside the function.
- The return value is ignored, so count is never reassigned.
Mistakes With Parameter Changes
Common mistake Expecting a change to a primitive parameter to update the caller's variable
What it looks likeInside add_ten, n = n + 10 and then assuming count now reads 15 after the call.
Why it failsThe parameter starts as a copy of the caller's value. Changes to it stay inside the subroutine.
Do this insteadIf the caller needs a new value, have the subroutine return it and assign the result, as in count = add_ten(count).
Common mistake Treating the parameter as the same storage as the caller's variable
Why it failsOnce execution starts, a parameter behaves much like a local variable, so it is a fresh name even though it began with the caller's value.
Do this insteadRead the parameter as a separate name with a starting value, and track the caller's variable separately.
Global Variables and Effects Beyond the Subroutine
A variable defined outside a subroutine is treated differently from a parameter. It exists independently of the subroutine and is accessible to other parts of the program. Such a variable is called global to the subroutine, in contrast to the local variables defined inside it. A global variable can be used throughout the class in which it is defined and, if it is not declared private, in other classes as well.
Because the variable is shared, changes made to it inside a subroutine can have effects that extend beyond that subroutine. The diagram below follows one such change from the subroutine body to other parts of the program that read the same variable later.
7- total is defined outside the function, so it exists before any call.
- The global statement tells the function to change the outside total rather than create a new local name.
- Each call changes the shared value, so the effect is visible after the function returns.
Global Variables as Hidden Interface
Using a global variable inside a subroutine turns the variable into part of that subroutine's interface. The subroutine is communicating with the rest of the program through the shared variable, not only through its parameter list. The source describes this as a kind of sneaky, back-door communication. It is less visible than communication through parameters, and it risks breaking the principle that a black box should have an interface that is straightforward and easy to understand. A reader who sees only the subroutine header will not know that the shared variable is being read or changed.
| Feature | Data passed as a parameter | Data read or written as a global |
|---|---|---|
| Visible in the subroutine header | Yes | No |
| Caller must know about it | Yes, it is in the call | Yes, but it is easy to miss |
| Effects reach outside the subroutine | Only through the return value or passed data | Yes, changes are shared |
| Interface clarity | Straightforward | Weakened |
How a parameter and a global variable differ as part of a subroutine's interface
Judging When a Global Is Reasonable
The source is careful not to forbid the practice. Global variables are not always bad inside subroutines. If the global variable is treated as part of the subroutine's interface, the communication is acceptable. The source recommends asking, before you use a global variable in a subroutine, whether it is really necessary. A practical check is to ask whether the same data could be passed in as a parameter and, if so, whether passing it would make the subroutine's dependencies clearer.
Before reaching for a global variable, ask whether it is really necessary. If you do use one, treat it as part of the subroutine's interface and make that dependency visible to anyone reading the code.
Practice Tracing Names Inside Subroutines
A caller has total = 10 and calls a subroutine whose parameter is x. The subroutine sets x = x * 2 and then sets a local variable y = x + 1 and returns. What does the caller's total hold after the call? Name each kind of variable that the subroutine uses.
Hints
- Ask which name in the subroutine was created to receive the caller's value.
- Primitive parameters receive a copy, so changing x does not change total.
- The local variable y exists only for the work of the subroutine.
Key Takeaways
- A subroutine can use three kinds of names: local variables it declares itself, formal parameters that receive values from the caller, and variables defined outside it that already exist and remain reachable.
- For primitive types, a formal parameter behaves like a local variable once execution starts, so changing it does not change the caller's variable.
- A global variable is shared, so a change made inside a subroutine can be seen by other parts of the program, and this makes it part of the subroutine's interface.
- Use a global variable inside a subroutine only after asking whether it is really necessary, and make its role visible to anyone reading the code.