Concepts / Static Member Variables in Classes

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.

  • Programming
Interactive lab

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

  1. Before a function runs, Python marks every name assigned inside it as local to that function, unless it is declared global or nonlocal.
  2. Reading a name searches the local namespace, then enclosing functions, then the module's globals, then the built-ins; the first hit wins.
  3. A local name read before it has a value raises UnboundLocalError (Python does not fall back to the global).
  4. global x makes assignments go to the module namespace; nonlocal x makes them go to the nearest enclosing function.
  5. 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.

Educational simulation

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?

  • 5

  • 15

  • It cannot be determined

Reveal answer
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.

Names a subroutinecan useLocal variabledeclared inside, for itsown useFormal parameterreceives a value from thecallerOutside variableexisted before the call,reachableis one ofis one ofis one of
Which names can the subroutine see, and which of those belong to the subroutine itself versus the rest of the program?

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.

countcaller's variablecount5nparameter, holds 5n15value copied intoparameter changed, caller unaffected
When the subroutine changes its parameter, what happens to the caller's original variable, and where does the copy live?
Worked example

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?

  1. 1
    Start of the call

    The caller has count holding 5. The value is dropped into the parameter n, which now also holds 5.

  2. 2
    Inside 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.

  3. 3
    Return 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.

Result

count holds 5 after the call. The change to n never reached the caller.

python
Output (expected)
5
Notes on the code
  • 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 like

    Inside add_ten, n = n + 10 and then assuming count now reads 15 after the call.

    Why it fails

    The parameter starts as a copy of the caller's value. Changes to it stay inside the subroutine.

    Do this instead

    If 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 fails

    Once 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 instead

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

Subroutine bodywrites the shared variabletotalglobal, sharedOther methods in theclassread the new valueOther classesonly if not declaredprivatechangesseen byseen by
After the subroutine runs, which other parts of the program see the changed value, and how does the change travel there?
python
Output (expected)
7
Notes on the code
  • 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.

FeatureData passed as a parameterData read or written as a global
Visible in the subroutine headerYesNo
Caller must know about itYes, it is in the callYes, but it is easy to miss
Effects reach outside the subroutineOnly through the return value or passed dataYes, changes are shared
Interface clarityStraightforwardWeakened

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.

Best practice

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

EASY

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.