COGNITION FOUNDRYLEARN. BUILD. UNDERSTAND.
PY-016 / Where names belong◷ 19 min read + practice

Scope and predictable functions

A value inside a function has to live somewhere. Python uses scopes to decide which name a line of code refers to. Learning those rules helps you spot bugs that otherwise look mysterious.

By the end, you can: Reason about local scope and write functions that avoid relying on changing external variables.
Before this lesson: PY-015 · PY-004

Names defined inside a function are local

When you assign a name inside a function, that name normally belongs to that function call. After the call finishes, the name is not automatically available to the surrounding script.

Parameters are also local names. A later call receives its own parameter values, even though it uses the same function definition. That is why one function can handle different inputs independently.

An outer name can be read, but hidden

Python can read an outer name from inside a function when there is no local binding for it. But if you assign to a name within the function, Python normally treats it as local throughout that function.

This is called shadowing when two scopes use the same name. The local message and outer message can refer to different values without replacing each other.

Avoid accidental shared state

A function that reads a changing outer variable can give different results for the same arguments. This makes it harder to understand a bug from the call alone.

A simple remedy is to pass the needed value as a parameter and return the result. Explicit dependencies are much easier to unit-test and later reuse inside browser tools or desktop apps.

Global modification is possible, but use sparingly

The global keyword can direct assignment to a module-level name, but a hidden change can affect code far from the function. It is usually clearer for small teaching utilities to return an updated value and let the caller assign it.

Scope does not make mutable objects immutable: a function that receives a list can still modify its contents. For predictable helpers, document whether you mutate an input or create a new result.

Try it yourself.

Choose a starting example or write something completely different. You control the code; the Run button executes what is in the editor, not a hidden preset.

CF Python PlaygroundBrowser-based Python
Ready to load Python
A local calculation

Create a local name and return its useful result.

Syntax colours highlight Python code. Tab inserts four spaces; press Escape then Tab to leave the editor. Ctrl+Enter runs the current code.
OUTPUT / ERRORS
Press Run Python to execute the editable code.
NOW MAKE IT YOURS

Try printing result outside the function and inspect the NameError. Then undo it.

Need a hint?

Only the returned value is passed back to the calling scope.

On first Run, a pinned Python runtime downloads from jsDelivr. Code executes locally in your browser's Web Worker, not on our server. It cannot inspect your normal PC files or Windows hardware. Avoid entering secrets. Heavy/infinite loops are stopped by a time limit.

Check your understanding.

Try answering each question before opening its reference answer. Explain the idea aloud or change an example to test it for yourself.

  1. Why does result created inside a function usually not exist after the function returns?

    View answer

    Assignments inside a function normally create local names that are visible only within that call. The surrounding script does not gain a new name just because the function created it. If the caller needs the computed value, the function should return it and the caller can store it under a name such as answer = square(6).

  2. What makes def total(price, rate): ... generally easier to test than a function that reads a changing module-level rate?

    View answer

    A function receiving price and rate explicitly makes both inputs visible at the call. You can test exactly total(100, 0.2) without first modifying a hidden module-level setting. A helper that secretly reads a global rate could change behaviour if unrelated code updates that setting, making tests fragile and problems harder to reproduce.

Where this leads.

Real users provide imperfect input. Next we will reject invalid values thoughtfully and handle expected failures without hiding programming errors.