Python Arguments and Scope: Parameters, LEGB, and Worked Examples
Trace how Python binds arguments, handles mutation and rebinding, and resolves names through LEGB. Runnable examples turn scope rules into predictable output.
KnowledgeGate Team
Exam prep & CS education

A function call binds objects to new local names, while nested functions read names through the LEGB lookup order. Argument binding, mutation, rebinding, and lookup are separate operations; tracing them in order makes both output and errors predictable.
Parameters and arguments: the vocabulary first
A parameter is a name written in a function definition. An argument is the value or expression supplied in a call. Consider this complete function:
def record_attempt(topic, score=0, *, reviewed=False):
status = "reviewed" if reviewed else "pending"
return topic, score, status
print(record_attempt("scope"))
print(record_attempt("scope", 8, reviewed=True))Here, topic, score, and reviewed are parameters. In record_attempt("scope", 8, reviewed=True), "scope" and 8 are positional arguments, while reviewed=True is a keyword argument. score=0 and reviewed=False are defaults used when those arguments are omitted. Because reviewed appears after *, it is keyword-only.
The official Python tutorial on defining functions documents these call and default-parameter rules. The first call returns ("scope", 0, "pending"). The second returns ("scope", 8, "reviewed"), because the explicit positional and keyword arguments replace both defaults.
How Python binds positional, keyword, default, *args, and **kwargs
Read a call in a practical order: match positional arguments from left to right, apply explicit keyword arguments, fill omitted optional parameters from defaults, then collect any supported extras. A parameter cannot receive two values. Therefore, record_attempt("scope", topic="functions") raises TypeError because topic is assigned both positionally and by keyword.
def build_order(customer, *items, priority=False, **notes):
return customer, items, priority, notes
result = build_order("Asha", "book", "pen", priority=True, city="Pune")The exact result is ("Asha", ("book", "pen"), True, {"city": "Pune"}). customer gets "Asha", items gets the tuple ("book", "pen"), priority gets True, and notes gets the dictionary {"city": "Pune"}. The names args and kwargs are conventions, not Python keywords. Prefer explicit parameters when you already know the accepted inputs.
What is actually passed: mutation versus rebinding
Saying only that Python uses “pass by reference” hides the important behaviour. A parameter becomes a local name bound to the same object supplied by the caller. Mutation changes that shared object, while rebinding changes what the local name refers to.
def update(items, n):
items.append(4)
n += 1
nums = [1, 2, 3]
count = 3
update(nums, count)After the call, nums is [1, 2, 3, 4] because append mutated the shared list. count remains 3 because n += 1 rebound only the local name n. Return a new value when the caller needs a scalar updated, and document intentional list or dictionary mutation. The Coding & Skills category is a useful next stop for structured programming practice.
Python scope and the LEGB lookup order
LEGB means Local, Enclosing, Global, Built-in. Python resolves a name from the nearest applicable scope, not from the place where a function happens to be called. The Python execution model gives the formal name-resolution rules.
tax = 18
def invoice(subtotal):
discount = 10
def total():
fee = 50
return subtotal * (1 - discount / 100) * (1 + tax / 100) + fee
return total()
print(invoice(2000))Inside total, fee=50 is local. subtotal=2000 and discount=10 are enclosing bindings created by invoice. tax=18 is global. The trace is 2000 * 0.90 = 1800.0, 1800.0 * 1.18 = 2124.0, then 2124.0 + 50 = 2174.0. A name such as round would come from the built-in scope, although this calculation does not need it.

Rebinding names with nonlocal and global
nonlocal makes an assignment target an existing binding in the nearest enclosing function scope. global makes an assignment target the module-level binding. Neither declaration is required merely to read a name.
def make_counter(start=3):
count = start
def next_count(step=2):
nonlocal count
count += step
return count
return next_count
counter = make_counter()
print(counter())
print(counter())The calls print 5 and 7. The enclosing count persists between calls. For contrast, if module-level calls = 0, a function that performs calls += 1 must first declare global calls; one call then changes that module binding to 1. In ordinary code, return values or small state-holding objects are often easier to test than hidden global writes.
Common failures: mutable defaults, shadowing, and local-name errors
Use Python Functions Deep Dive: *args, **kwargs, the Mutable Default Trap and LEGB Scope for the complete three-call mutable-default trace plus closures and late binding. At call time, the key binding rule is that a mutable default is created when def runs, so every omitted argument reuses that object. A signature such as def add(item, box=[]) therefore lets one call's append affect the next. Bind None and allocate inside the call instead:
def add(item, box=None):
if box is None:
box = []
box.append(item)
return boxAnother trap starts with global score = 10, then reads score inside a function before assigning score = 20 there. Python treats score as local throughout that function, so the early read raises UnboundLocalError. Passing the value in and returning the replacement is cleaner than reflexively adding global.
Shadowing can also break familiar functions. After len = 4, the call len([1, 2]) raises TypeError: 'int' object is not callable; use a name such as length. A missing name raises NameError, while UnboundLocalError is the more specific case of a local name read before it has a value.
How exams and interviews test arguments and scope
Assessment questions usually ask for output prediction, error identification, parameter binding, default-object reuse, or the name selected by LEGB. Work through each shape until you can predict the result before running the code.
- Change the call to
record_attempt(topic="scope", score=8, reviewed=True). Answer: all three arguments now bind by keyword, and the result is("scope", 8, "reviewed"). Predict the first two calls to
counter(). Answer: they return5and7;countis an enclosing binding updated throughnonlocal.- Explain why a list default can retain data between calls, then repair the signature. Answer: the list is created once when
defruns; usebox=Noneand createbox = []inside the function when needed. Repair the score example. Answer: write
def update_score(score): score = 20; return score. Thenscore = update_score(10)produces20.
For function definitions, return versus print, and composing calls, use Functions in Python: Parameters, Return Values, Scope and Examples. Argument binding, shared-object mutation, rebinding, and LEGB lookup are the next layer once those basics are secure.
Short version and the next step
Arguments supply objects.
Parameters bind local names.
Mutation can affect a shared mutable object.
Rebinding normally stays local.
LEGB decides where a read finds a name.
Rerun each example after changing one argument or one scope at a time, then return to the Python course for the full language sequence. If your next goal is coding rounds and interview practice, the Coding for Placements course is the more focused follow-on.
Keep learning

Exceptions in Python: Handle Errors with try, except and finally
Learn how Python exceptions change control flow, how to catch expected failures without hiding bugs, and when to use else, finally, raise, and custom exceptions.

Context Managers in Python: with, __enter__, __exit__ and contextlib
Learn how Python's with statement guarantees cleanup, then build and trace class-based and generator-based context managers with runnable examples.

Python Control Flow and Loops: if, for, while and Worked Traces
Learn to trace Python branches and loops without skipping a state change. Worked examples cover GCD, break and continue, loop else, nested loops, and common mistakes.

Python for GATE: CS vs DA Syllabus, Past-Paper Evidence and a 10-Week Plan
Choose the right Python preparation path for GATE CS or DA, then use two worked traces and three diagnostic gates to test your progress.