Advanced Parameters and Scope
Now that you've learned the basics of functions, let's explore some advanced techniques. How many ways can you write parameters? What does "variable scope" mean? What is recursion? These concepts will take your understanding of functions to the next level.
1. Default Parameters
Default parameters let you omit arguments when calling a function:
Example: Basic Default Parameters
def greet(name, greeting="Hello"):
print(f"{greeting}, {name}!")
greet("Alice") # Hello, Alice!
greet("Bob", "Hi") # Hi, Bob!
greet("Charlie", "Hey") # Hey, Charlie!
⚠️ The Default Parameter Pitfall
Default parameter values are evaluated only once, at function definition time, not each time the function is called. If the default is a mutable object (like a list or dictionary), this can lead to unexpected behavior:
Example: Mutable Default Pitfall
def add_item(item, items=[]): # ❌ Dangerous!
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] — not ['banana']!
print(add_item("orange")) # ['apple', 'banana', 'orange']
Why: The empty list items=[] is created once at function definition. Every subsequent call appends to the same list.
Correct approach: Use None as the default and create a new list inside the function:
Example: Safe Default Parameter
def add_item(item, items=None): # ✅ Safe
if items is None:
items = []
items.append(item)
return items
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['banana']
print(add_item("orange")) # ['orange']
None, numbers, strings, tuples). Never use mutable objects like lists or dictionaries as defaults.
2. Keyword Arguments
When calling a function, you can specify values by parameter name, allowing you to pass arguments out of order:
Example: Keyword Arguments
def create_user(name, age, city):
print(f"User: {name}, {age} years old, from {city}")
# Positional arguments — must be in order
create_user("Alice", 25, "Beijing")
# Keyword arguments — specify parameter names, order doesn't matter
create_user(age=30, city="Shanghai", name="Bob")
create_user(name="Charlie", city="Guangzhou", age=22)
# Mixed — positional first, keyword second
create_user("Diana", city="Shenzhen", age=28)
# create_user(name="Eve", 35, "Chengdu") ❌ Keyword args can't precede positional
3. *args: Variable Positional Arguments
If you don't know how many arguments a function will receive, use *args:
Example: *args for Sum Function
def sum_all(*numbers):
"""Accepts any number of numbers and returns their sum"""
total = 0
for n in numbers:
total += n
return total
print(sum_all(1, 2)) # 3
print(sum_all(1, 2, 3, 4, 5)) # 15
print(sum_all()) # 0 — no arguments also works
*args packs all positional arguments into a tuple:
Example: Inspecting args
def show_args(*args):
print(f"Received {len(args)} arguments: {args}")
for i, arg in enumerate(args, 1):
print(f" #{i}: {arg}")
show_args("apple", "banana", "orange")
4. **kwargs: Variable Keyword Arguments
**kwargs accepts any number of keyword arguments and packs them into a dictionary:
Example: **kwargs for Building User Profiles
def create_profile(**info):
"""Create a user profile, accepting any amount of info"""
print("User Profile:")
for key, value in info.items():
print(f" {key}: {value}")
create_profile(name="Alice", age=25, city="Beijing", job="Engineer")
Output:
User Profile:
name: Alice
age: 25
city: Beijing
job: Engineer
Example: Combining *args and **kwargs
def advanced_function(a, b, *args, **kwargs):
print(f"Positional: a={a}, b={b}")
print(f"Extra positional: {args}")
print(f"Keyword: {kwargs}")
advanced_function(1, 2, 3, 4, 5, name="Alice", age=25)
# Positional: a=1, b=2
# Extra positional: (3, 4, 5)
# Keyword: {'name': 'Alice', 'age': 25}
normal parameters → *args → default parameters → **kwargs
5. Scope: LEGB Rule
Where can a variable be accessed? This is determined by scope. Python has four levels of scope:
Example: LEGB Scope Nesting
# Global scope
x = 10 # Global variable
def outer():
# Enclosing function scope
x = 20 # This is outer's local x, different from the global x
def inner():
# Local scope
x = 30 # This is inner's local x
print(f"inner: {x}") # 30
inner()
print(f"outer: {x}") # 20
outer()
print(f"global: {x}") # 10
LEGB stands for:
- Local — inside the current function
- Enclosing — the enclosing function's locals
- Global — module level
- Built-in — Python built-in functions (like
print,len)
Python looks up variables in L → E → G → B order.
global and nonlocal
To modify a global variable inside a function, use global:
Example: Using global
count = 0
def increment():
global count # Declare intent to modify the global variable
count += 1
increment()
increment()
print(count) # 2
global. Global variables make code harder to understand and debug. Functions should receive input through parameters and return output through return, not secretly modify global variables.
6. Recursion Basics
Recursion is when a function calls itself. The core idea: break a big problem into smaller, similar sub-problems.
Example: Recursive Factorial
def factorial(n):
"""Calculate n! (n factorial)"""
if n <= 1:
return 1 # Termination condition
return n * factorial(n - 1) # Recursive call
print(factorial(5)) # 120 (5×4×3×2×1)
Execution:
factorial(5) = 5 * factorial(4)
= 5 * 4 * factorial(3)
= 5 * 4 * 3 * factorial(2)
= 5 * 4 * 3 * 2 * factorial(1)
= 5 * 4 * 3 * 2 * 1
= 120
Example: Fibonacci Sequence (Difficulty ⭐⭐⭐)
def fibonacci(n):
"""Return the nth Fibonacci number (starting from 0)"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# Print first 10
for i in range(10):
print(fibonacci(i), end=" ")
# Output: 0 1 1 2 3 5 8 13 21 34
Common Use Cases
- Default parameters: Configuration options, timeout values, callback functions.
*args: Math aggregation functions (sum, average), log formatting.**kwargs: Function wrappers, config merging, database query parameters.- Recursion: Directory tree traversal, nested JSON parsing, mathematical formulas (factorial/fibonacci).
- Scope: Understanding closures (next lesson), understanding variable lifetime.
❓ FAQ
*args and kwargs?*args and kwargs are just naming conventions — the asterisks are what matter. You could write *numbers, **options. But following convention is recommended so other Python programmers immediately understand.
⚠️ Q: Recursion vs loops — which is better? A: If a loop can solve the problem, prefer the loop. Loops have better performance, won't cause stack overflow, and are easier to understand. Recursion suits two scenarios: tree structure traversal (directories, JSON) and mathematically recursive definitions (factorial, Fibonacci). Recursion is more concise but harder to understand.
Q: Why can I read a global variable inside a function but not modify it? A: When reading, Python finds the global variable by following LEGB rules. But when assigning, Python creates a new local variable by default — x = 10 inside a function is treated as "create a local variable x." If you truly need to modify a global, use global. But that usually means your design could be improved.📖 Summary
- Default parameters add flexibility; never use mutable objects as defaults
- Keyword arguments let you pass by name, not position
*argspacks extra positional args into a tuple;**kwargspacks keyword args into a dict- Parameter order: normal →
*args→ defaults →**kwargs - LEGB rule determines variable lookup order: Local → Enclosing → Global → Built-in
globalmodifies global variables from inside a function; minimize its use- Recursion is a function calling itself; needs a termination condition and a recursive condition
📝 Exercises
-
Basic (Difficulty ⭐): Write a function
multiply(*nums)that accepts any number of numbers and returns their product. If no arguments, return 0. -
Intermediate (Difficulty ⭐⭐): Write a function
create_student(name, **scores)that receives a student name and any number of subject scores (keyword arguments), calculates and returns the average. Example call:create_student("Alice", Chinese=85, Math=92, English=78). -
Challenge (Difficulty ⭐⭐⭐): Write a recursive function
sum_digits(n)that calculates the sum of digits of a positive integer. For example,sum_digits(123)returns1+2+3=6. Hint: Modulo 10 gives the last digit; floor division by 10 removes it. Termination condition: n < 10.



