Function Arguments (*args, **kwargs & Enforced Syntax)

Module 2 • Session 13 • Allocation: 1 Contact Hr

positional keywords *args **kwargs / and * specifiers

Python parameters determine how incoming dynamic values are structured, validated, and processed inside functions. Mastering positional mapping, keyword bindings, fallback defaults, and syntax enforcement specifiers allows you to design flexible and robust signatures.

1. Overview of Parameter Passing Mechanisms

Argument Category Syntax Example Primary Behavior
Positional Arguments func(val1, val2) Values are bound to parameters based on exact position order.
Keyword Arguments func(name="Alice") Values are bound explicitly by parameter name regardless of positional order.
Default Parameters def f(x=10): Assigns fallback values when arguments are omitted at call site.
Arbitrary Positional (*args) def f(*args): Packs surplus positional inputs into a standard tuple.
Arbitrary Keywords (**kwargs) def f(**kwargs): Packs surplus named arguments into a standard dict.

2. Default Parameters & The Mutable Default Trap

Default values are evaluated once when the function definition is executed, not each time the function is called. Using mutable objects (lists, dictionaries, sets) as defaults causes state to leak across function invocations.

# Dangerous anti-pattern: mutable default list def append_item_buggy(element, target_list=[]): target_list.append(element) return target_list print(append_item_buggy(1)) # Output: [1] print(append_item_buggy(2)) # Output: [1, 2] (Unexpected persistent state!) # Recommended idiom: Sentinel None pattern def append_item_safe(element, target_list=None): if target_list is None: target_list = [] target_list.append(element) return target_list print(append_item_safe(1)) # Output: [1] print(append_item_safe(2)) # Output: [2]
Avoid Mutable Defaults: Always default mutable arguments to None and initialize them conditionally inside the function body to prevent cross-call variable contamination.

3. Flexible Signatures: `*args` and `**kwargs`

When building wrapper tools, decorators, or dynamic pipeline utilities, you can collect variable amounts of positional and keyword inputs cleanly.

def build_user_profile(username, *skills, **metadata): """ - username: Mandatory positional argument - *skills: Variable positional inputs packed into a tuple - **metadata: Variable keyword arguments packed into a dictionary """ return { "user": username, "skills_list": list(skills), "extra_info": metadata } profile = build_user_profile("dev_pro", "Python", "FastAPI", role="Backend Engineer", active=True) print(profile) # Output: {'user': 'dev_pro', 'skills_list': ['Python', 'FastAPI'], 'extra_info': {'role': 'Backend Engineer', 'active': True}}

4. Position-Only (`/`) & Keyword-Only (`*`) Boundaries

Python allows explicitly restricting argument syntax using the slash (/) and asterisk (*) specifiers in signatures:

  • Before /: Parameters must be passed positionally. Keyword syntax is strictly prohibited.
  • After *: Parameters must be passed using keyword pairs (key=value).
def configure_pipeline(name, /, *, batch_size=64, debug=False): return f"Pipeline: {name} | Batch: {batch_size} | Debug: {debug}" # Valid invocation print(configure_pipeline("ETL_Main", batch_size=128, debug=True)) # Invalid calls that raise TypeError: # configure_pipeline(name="ETL_Main") # TypeError: positional-only argument passed as keyword # configure_pipeline("ETL_Main", 128) # TypeError: takes 1 positional argument but 2 were given
Design Tip: Use / for parameters whose variable names are internal details or subject to change, and use * for flags and configuration toggles to prevent accidental positional misplacement.

5. Unpacking Iterables and Mappings

You can unpack lists, tuples, or dictionaries directly into function parameters using the * and ** operators at the call site.

def calculate_box_volume(length, width, height): return length * width * height # Unpacking a positional sequence (* list / tuple) dimensions = [10.0, 4.0, 2.5] vol1 = calculate_box_volume(*dimensions) # Unpacking a dictionary mapping (** dict) kwargs_dict = {"length": 12.0, "width": 5.0, "height": 3.0} vol2 = calculate_box_volume(**kwargs_dict) print(f"Volume 1: {vol1}, Volume 2: {vol2}")

🏋️ Try It Yourself: Practice Challenges

Challenge 1: Safe Log Aggregator

Write a function log_messages(level, *messages, timestamp=None) that prints each message prefixed with the logging level and time.

Challenge 2: Strict Positional & Keyword Boundary

Define a function format_currency(amount, /, *, currency="USD", symbol="$") that strictly enforces positional amounts and keyword options.

Challenge 3: Dynamic Data Unpacker

Create a function that calculates total invoice cost and pass arguments using both iterable (*) and dictionary (**) unpacking.

⚡ Interactive Sandbox (Argument Engine)
Console Output:
Click "Run Code" above to execute interactive function scripts...

📝 Knowledge Check Quiz

1. Why is defining `def fn(data=[])` problematic in Python?
2. In a function header `def fn(a, /, b, *, c):`, which parameter is strictly positional-only?
3. What data structure is created when arbitrary keyword arguments (`**kwargs`) are collected?
Advertisement
Horizontal Ad Banner Slot (728x90 / Responsive)