Loading Page Content...
Preparing courses, live labs, and learning resources.सामग्री लोड हो रही है, कृपया प्रतीक्षा करें...
Preparing courses, live labs, and learning resources.सामग्री लोड हो रही है, कृपया प्रतीक्षा करें...
In Python, function parameters can be supplied with default values to make arguments optional. While intuitive at first glance, parameter evaluation in Python operates under a crucial architectural mechanism that catches even experienced engineers off-guard: Default arguments are evaluated exactly once at definition time (when the function is first loaded into memory), NOT every time the function is called.
This mechanism leads to one of Python's most notorious bugs: the Mutable Default Argument Trap. In this lesson, you will master function parameter mechanics, inspect __defaults__ in memory, implement the None sentinel pattern, and utilize positional-only (/) and keyword-only (*) markers.
Imagine a shared community chai thermos in a busy Indian office pantry:
+-------------------------------------------------------------------------+
| THE SHARED OFFICE CHAI THERMOS ANALOGY |
+-------------------------------------------------------------------------+
| |
| The Mutable Trap: def pour_chai(cup, tray=[]): |
| ──> Python creates ONE single physical tray in RAM at compile time! |
| |
| Employee 1 (Aman): Calls pour_chai("Masala Chai") |
| ──> Puts cup on the shared tray: ['Masala Chai'] |
| |
| Employee 2 (Priya): Calls pour_chai("Ginger Tea") |
| ──> Expects a fresh tray, but receives Aman's leftover tray! |
| ──> Tray now holds: ['Masala Chai', 'Ginger Tea'] (Data Leakage!) |
| |
| The Sentinel Fix: def pour_chai(cup, tray=None): |
| ──> if tray is None: tray = [] |
| ──> Every employee gets their own fresh, private, clean tray! |
| |
+-------------------------------------------------------------------------+When you write tray=[] in the def header, Python binds a single list in heap memory when the file is imported. Every call that omits the parameter mutates that exact same list object.
__defaults__ TupleTo understand why this occurs, inspect the function object's internal attribute __defaults__:
Because Python functions are first-class objects created when the def statement executes, the default value expression is evaluated once and attached to func.__defaults__.
To safely supply a fresh mutable container (list, dictionary, set) on every call, use None as the default argument:
+------------------------------------+------------------------------------+ | Anti-Pattern (Dangerous Mutation) | Gold-Standard (Sentinel Pattern) | +------------------------------------+------------------------------------+ | def log_event(msg, events=[]): | def log_event(msg, events=None): | | events.append(msg) | if events is None: | | return events | events = [] # Fresh! | | | events.append(msg) | | | return events | +------------------------------------+------------------------------------+
Expected Output:
/) vs Keyword-Only (*) ParametersIntroduced in Python 3.8 (PEP 570), the forward slash / indicates that parameters before it are positional-only, while the asterisk * indicates that parameters after it are keyword-only:
How data and code flow step-by-step
Expected Output:
Another classic pitfall is calling a function like datetime.now() in the parameter definition:
Expected Output:
| Practice | Dangerous Anti-Pattern | Recommended Gold Standard |
|---|---|---|
| Mutable Defaults | def fn(data=[]): | def fn(data=None): if data is None: data = [] |
| Dict Defaults | def config(options={}): | def config(options=None): if options is None: options = {} |
| Timestamps | def stamp(time=datetime.now()): | def stamp(time=None): if time is None: time = datetime.now() |
| API Parameter Locks | Exposing internal variable names blindly | Use / for positional-only to safely rename parameters later |
| Flags & Booleans | def process(x, False, True): (Cryptic!) | def process(x, *, dry_run=False, verbose=True): (Clear!) |
[], {}, or set() as defaults leads to state sharing across independent invocations.None, then instantiate val = [] or val = {} inside the function body./): Parameters to the left of / cannot be called via name=val.):** Parameters to the right of * must be specified explicitly via name=val`.A banking system in Mumbai records customer transactions. A junior developer wrote the following flawed function:
Because of the mutable default argument trap, transactions from different bank customers are leaking into each other's audit logs!
Your task:
record_transaction using the Sentinel Pattern (audit_log=None).account_id and amount must be passed positionally.txn_type must be passed as a keyword argument (*, txn_type="Credit").In the next lesson, we will continue your intermediate Python journey with Closures (2: Functions Deep Dive).
Continue learning with hands-on practice, examples, and exercises in the upcoming topic.
| Previous Lesson | Next Lesson |
|---|---|
| Arguments Recap (*args, **kwargs) | Closures |
Test your understanding of this lesson with 5 questions. Each question has one correct answer.