1. What Is It?

A function is a named block of code you can invoke with arguments to get a result. is the rule that decides which names are visible from where. Together they form the basic unit of reuse: a function does one thing, takes inputs, returns an output, and its internals are hidden from the rest of the program.

Without functions, every program is one long script with no reuse and no way to isolate bugs. Without rules, every collides with every other — you'd need globally unique names for everything. Understanding both is what separates code that works once from code that works as a system.

Quick check

A junior developer on your team writes a 500-line script to process API responses. All logic lives at the top level, and variables like data, result, and index are reused throughout. The team reports that fixing one bug keeps breaking unrelated parts of the script. What is the most direct cause of this problem?

Choose one answer

2. How It Works

When a function is called:

  1. A new frame is allocated for its local variables and parameters.
  2. Arguments are bound to parameters (by value or by , depending on the language).
  3. The function body executes.
  4. On return, the result is passed back, the frame is torn down, and locals disappear.
# Python
def area(width, height):
    result = width * height       # 'result' is local to this frame
    return result

a = area(3, 4)                    # a = 12
# 'result' no longer exists here

Scope: where a name is visible

A is the region of code where a name can be resolved. Most languages use lexical (static) : you can determine a name's scope by reading the source. It doesn't depend on who called whom at runtime.

Python's scope lookup follows the LEGB rule:

  • Local (inside the current function)
  • Enclosing (inside any surrounding function, for closures)
  • Global ( top level)
  • Built-in (print, len, etc.)
x = "global"
def outer():
    x = "enclosing"
    def inner():
        # x = "local"  # uncomment to shadow
        print(x)        # "enclosing" — LEGB lookup finds it
    inner()
outer()

Java and C++ use block scope: a name declared inside { } exists only within those braces and any nested blocks.

void f() {
    for (int i = 0; i < 10; i++) {
        int temp = i * 2;    // 'temp' visible only inside this loop body
    }
    // 'i' and 'temp' are both gone here
}

Parameters and arguments

  • Parameter — the name in the function's definition.
  • Argument — the value you pass in at the call site.
# Python — positional, keyword, default, variadic
def greet(name, greeting="Hello", *extras, **options):
    ...

greet("Ada")                        # positional
greet(name="Ada", greeting="Hi")    # keyword
greet("Ada", "Hi", "and", "bye")    # *extras captures the rest

A call creates locals; return removes them

The call area(3, 4) binds arguments, computes a local result, and returns 12 to the caller.

Calling area with three and four creates a frame and binds width and height.

Text version
  1. Calling area with three and four creates a frame and binds width and height.
  2. Multiplication stores twelve in result, a name local to this call.
  3. Return sends twelve back to the caller before the frame is removed.
  4. The caller stores twelve in a, while the function parameters and local result disappear.
Quick check

A backend developer writes the following Python function and calls it from a route handler: x = 'global' def process_request(): x = 'local' def build_response(): return x return build_response() result = process_request() What value does result hold, and why?

Choose one answer

3. What You Actually Need to Know

  • Locals die when the function returns. In C++, returning a pointer to a local is undefined behavior (the slot is reused). In Java/Python, returning a to a local is fine because the lives on the — only the local name disappears.
  • Shadowing silently replaces outer names. A local x inside a function hides a -level x. Useful, but if you meant to modify the outer one, you need global x (Python) or pass it in / return it (Java/C++).
  • Assignment in Python creates a local. x = 5 inside a function always declares a local x. To modify an enclosing / global one, use nonlocal or global. This surprises people coming from other languages.
  • Don't use default arguments in Python. def f(xs=[]) shares one list across every call that omits the argument — a classic footgun. Use xs=None and build inside the body.
  • Overloading vs. defaults. Java picks overload resolution; Python uses default arguments; C++ offers both. Same outcome, different style.
  • Pure functions are easier. A function that reads only its parameters and returns a value (no globals, no I/O, no mutation) is the easiest to test, reason about, and reuse. Push impurity to the edges of the program.
  • Debugging clues:
    • UnboundLocalError (Python) — you assigned to a name later in a function, so the interpreter treats it as local and reading it earlier fails.
    • NameError — name isn't in any visible (typo or missing import).
    • Value unexpectedly persists between calls → default argument or global state.
    • overflow → usually runaway ; sometimes very deep call chains.

A local name shadows outer names

With the doc's optional local assignment enabled, inner prints local while the enclosing and global bindings stay unchanged.

The outer function creates its own x without replacing the global binding.

Text version
  1. The outer function creates its own x without replacing the global binding.
  2. Outer calls inner, where the uncommented line makes a local x that hides the outer names.
  3. Python finds the local x first, so print outputs the word local.
  4. After both calls return, the module still has its original global x.
Quick check

A backend developer writes the following Python function to collect API request logs: def log_request(path, headers, log=[]): log.append({'path': path, 'headers': headers}) return log After several requests are handled, the developer notices the log keeps growing across unrelated calls even when they expect a fresh list each time. What is the root cause?

Choose one answer

4. Language Differences

AspectPythonJavaC++
First-class functionsYes (functions are objects)Kind of (method references, lambdas as functional interfaces)Yes (function pointers, lambdas, std::function)
Default argumentsYes (but beware mutable defaults)No (use overloading)Yes
Keyword argumentsYesNoNo
Variadic (*args, **kwargs)YesYes (String... names)Yes (variadic templates, ... for C-style)
Nested function definitionsYes (closures)Local classes / lambdas onlyLambdas; free functions must be at namespace scope
Overloading on typesNo (runtime dispatch via isinstance or singledispatch)YesYes
Lambda / anonymous functionlambda x: x+1 (limited to expressions)x -> x + 1[](int x) { return x+1; }

Python's dynamic dispatch means there's no overloading — the latest def greet(...) just overwrites the earlier one, silently.

Defaults, keywords, and overloads differ by language

Python supports defaults and keyword arguments; Java uses overloading; C++ supports defaults and overloading.

Python and C plus plus allow defaults, Java uses overloading, and a mutable Python default is shared across calls.

Text version
  1. Python and C plus plus allow defaults, Java uses overloading, and a mutable Python default is shared across calls.
  2. Of these languages, only Python lets callers pass arguments by keyword.
  3. Java and C plus plus overload by parameter list, but a second Python definition replaces the first one.
Quick check

A backend developer is migrating a Python API endpoint to Java. The Python function uses keyword arguments so callers can pass parameters in any order by name, like create_user(role='admin', name='Alice'). What is the Java equivalent approach to achieve similar flexibility?

Choose one answer

5. Tradeoffs & Decisions

  • Short functions vs. long functions. A function should do one thing at one level of abstraction. If you need a mental "section break" inside a function, that's a sign to extract another function.
  • Parameters vs. globals. Passing dependencies as parameters makes functions testable and explicit. Globals make the function shorter but couple it to the rest of the program — harder to test, harder to reuse.
  • By- vs. by-value parameters. Use by-value when the callee shouldn't affect the caller's data. Use by- when the data is large (avoid copy cost) or when mutation is the point. In C++, const T& is the common default for "don't copy, don't mutate."
  • Returning vs. mutating. Prefer returning new values over mutating parameters — especially for data you share across owners. Mutation is fine locally; aliased mutation is the source of many bugs.
  • Function length. There's no magic number, but anything over ~30–50 lines usually hides a sub-function trying to get out.

If you see a function taking 10+ parameters, it usually means the parameters want to be an . You'd choose an explicit return over mutation when callers might share the argument.

Quick check

A backend developer writes a function that updates a user record object. Multiple services in the application hold references to the same user object. Which approach is safer, and why?

Choose one answer

6. Interview Cheat Sheet

  • A function is a reusable block of code taking parameters and returning a value. Each call gets a new frame for its locals.
  • Lexical means a name's is determined by where it's written, not who calls it. Python uses the LEGB rule; Java and C++ use block scope.
  • Parameter is the name in the definition; argument is the value passed in.
  • default arguments in Python are a footgun — the default is created once at definition time, not per call.
  • Pure functions (no globals, no mutation, no I/O) are the easiest to test and reason about.
  • Assignment inside a Python function creates a local unless you use global / nonlocal.

Follow-ups:

  • "What's the difference between a parameter and an argument?" — Parameter is the in the definition; argument is the value supplied at call time.
  • "Why can't I modify a global from inside a Python function?" — Assignment creates a local. Use global (or better: pass it in and return it) to modify the global binding.
  • "What does returning a to a local mean in C++?" — Undefined behavior — the frame is gone and the slot will be overwritten.

Reading

On this page

My notes

Sign in to keep highlights and notes.

Glossary history

Click dotted jargon to save explanations here.