A Complete Guide to Python Function Arguments
Master Python function arguments from basic to advanced with simple explanations, interactive examples, and practical quizzes
What You’ll Learn Today
- Why Function Arguments Matter
- Positional Arguments – The Basics
- Keyword Arguments – Name Your Values
- Default Arguments – Smart Defaults
- Variable Arguments (*args) – Handle Any Number
- Keyword Variable Arguments (**kwargs) – Ultimate Flexibility
- Combining All Types – The Complete Picture
- Advanced Techniques – Pro Tips
- Test Your Knowledge – Interactive Quiz
- Common Questions Answered
Why Function Arguments Matter
Imagine you’re ordering pizza. You don’t just say “I want pizza.” You tell them the size, toppings, crust type, and delivery address. Function arguments work the same way – they’re the details that make your functions actually useful!
Function arguments are like ingredients in a recipe. You give your function these ingredients, and it creates something amazing. In Python, there are different ways to pass these ingredients, and knowing them all makes you a much better programmer.
Think about it this way: A function without arguments is like a vending machine that only gives you one type of snack. But a function with arguments? That’s like having a personal chef who can make anything you want!
When you define and call functions in Python, understanding arguments becomes super important. It’s the difference between writing rigid code and flexible, reusable code.
Positional Arguments – The Basics
Positional arguments are like standing in line at a coffee shop. The order matters! First person gets served first, second person gets served second. Same with positional arguments – first argument goes to first parameter, second to second, and so on.

Let’s Start Simple
Here’s the most basic example:
def introduce_person(name, age):
return f"Hi! I'm {name} and I'm {age} years old."
# Calling with positional arguments
result = introduce_person("Sarah", 25)
print(result)
See how “Sarah” automatically went to the name
parameter and 25
went to the age
parameter? That’s because of their position!
What Happens If We Switch Order?
Let’s see what happens when we mess up the order:
# Oops! Wrong order
result = introduce_person(25, "Sarah")
print(result)
That doesn’t make sense! This shows why order matters with positional arguments.
Real-World Example: Calculator Function
Let’s create something more practical:
def calculate_rectangle_area(length, width):
"""Calculate the area of a rectangle"""
area = length * width
return f"A rectangle with length {length} and width {width} has area {area}"
# Different rectangles
print(calculate_rectangle_area(10, 5))
print(calculate_rectangle_area(7, 3))
print(calculate_rectangle_area(15, 8))
Best Practices for Positional Arguments
- Keep the number of positional arguments small (ideally 2-4)
- Use clear, descriptive parameter names
- Put the most important arguments first
- Consider using keyword arguments for functions with many parameters
Keyword Arguments – Name Your Values
Keyword arguments are like labeled boxes. Instead of guessing what goes where based on order, you put a label on each value. This makes your code much clearer and less error-prone.
Think of it like ordering at a restaurant where you can say “I want the pasta with extra cheese and no mushrooms” instead of just saying “pasta, cheese, no mushrooms” and hoping they understand the order.

Basic Keyword Arguments
def create_profile(name, age, city, hobby):
return f"{name} is {age} years old, lives in {city}, and loves {hobby}."
# Using keyword arguments - order doesn't matter!
profile1 = create_profile(age=28, name="Alex", hobby="reading", city="New York")
profile2 = create_profile(city="Paris", hobby="cooking", name="Marie", age=32)
print(profile1)
print(profile2)
Notice how we mixed up the order completely, but Python still understood exactly what we meant because we used the parameter names!
Mixing Positional and Keyword Arguments
You can use both types together, but there’s an important rule: positional arguments must come first.
def book_flight(destination, departure_date, seat_class, meal_preference):
return f"Flight to {destination} on {departure_date}, {seat_class} class, {meal_preference} meal"
# Mixing positional and keyword arguments
flight1 = book_flight("Tokyo", "2024-07-15", seat_class="business", meal_preference="vegetarian")
flight2 = book_flight("London", departure_date="2024-08-20", seat_class="economy", meal_preference="regular")
print(flight1)
print(flight2)
What NOT to Do
This will cause an error because keyword arguments must come after positional ones:
# This will cause a SyntaxError!
# flight = book_flight(destination="Rome", "2024-09-10", "economy", "halal")
# ERROR: positional argument follows keyword argument
Understanding how to use function parameters and return values in Python becomes much easier when you master keyword arguments. They make your functions more readable and user-friendly.
Default Arguments – Smart Defaults
Default arguments are like having a smart assistant who knows your usual preferences. If you don’t specify something, they use what you normally like. If you do specify, they use your new choice.
Think of default arguments like your favorite coffee order. If you walk into your regular coffee shop and just say “the usual,” they know what to make. But if you want something different today, you can still specify exactly what you want.

Simple Default Arguments
def greet_customer(name, greeting="Hello", punctuation="!"):
return f"{greeting}, {name}{punctuation}"
# Using default values
print(greet_customer("Alice"))
# Changing just the greeting
print(greet_customer("Bob", "Hi"))
# Changing both optional parameters
print(greet_customer("Charlie", "Hey", ".."))
# Using keyword arguments to skip the middle parameter
print(greet_customer("Diana", punctuation="!!!"))
Practical Example: Email Function
Let’s create a function that sends emails with smart defaults:
def send_email(recipient, subject, message, sender="noreply@company.com", priority="normal"):
"""Send an email with default sender and priority"""
email_info = {
"to": recipient,
"from": sender,
"subject": subject,
"message": message,
"priority": priority
}
return f"Email sent!\\nTo: {recipient}\\nFrom: {sender}\\nSubject: {subject}\\nPriority: {priority}\\nMessage: {message}"
# Simple email with defaults
print(send_email("alice@example.com", "Meeting Tomorrow", "Don't forget our 2 PM meeting!"))
print("\\n" + "="*50 + "\\n")
# High priority email with custom sender
print(send_email(
"bob@example.com",
"URGENT: Server Down",
"The main server is down. Please check immediately!",
sender="admin@company.com",
priority="high"
))
Gaming Example: Character Creation
Let’s create a game character with default stats:
def create_character(name, character_class="Warrior", health=100, mana=50, level=1):
return {
"name": name,
"class": character_class,
"health": health,
"mana": mana,
"level": level
}
# Quick warrior with defaults
hero1 = create_character("Aragorn")
print(f"Hero 1: {hero1}")
# Custom mage
hero2 = create_character("Gandalf", "Mage", health=80, mana=150)
print(f"Hero 2: {hero2}")
# Experienced archer
hero3 = create_character("Legolas", character_class="Archer", level=5)
print(f"Hero 3: {hero3}")
Variable Arguments (*args) – Handle Any Number
Sometimes you don’t know how many arguments someone will give your function. It’s like being a waiter who doesn’t know if a table will order 2 dishes or 10 dishes. That’s where *args comes in handy!
The *args (which stands for “arguments”) lets your function accept any number of positional arguments. Think of it as a magic bag that can hold as many items as you throw into it.

Basic *args Example
def add_numbers(*numbers):
"""Add any number of numbers together"""
total = 0
for num in numbers:
total += num
return total
# You can pass any number of arguments!
print(add_numbers(5)) # One number
print(add_numbers(5, 10)) # Two numbers
print(add_numbers(1, 2, 3, 4, 5)) # Five numbers
print(add_numbers(10, 20, 30, 40, 50, 60)) # Six numbers
See how the same function handled 1 number, 2 numbers, 5 numbers, and 6 numbers? That’s the power of *args!
What Happens Inside the Function?
Let’s peek inside to see what *args actually contains:
def examine_args(*args):
print(f"Type of args: {type(args)}")
print(f"Args contains: {args}")
print(f"Number of arguments: {len(args)}")
for i, arg in enumerate(args):
print(f" Argument {i+1}: {arg}")
examine_args("apple", "banana", "cherry")
*args creates a tuple containing all the extra arguments. Pretty cool, right?
Mixing Regular Arguments with *args
def create_shopping_list(store_name, *items):
"""Create a shopping list for a specific store"""
print(f"Shopping List for {store_name}:")
print("-" * 30)
if not items:
print("No items in the list yet!")
else:
for i, item in enumerate(items, 1):
print(f"{i}. {item}")
# Different shopping lists
create_shopping_list("Walmart")
print()
create_shopping_list("Target", "milk", "bread", "eggs")
print()
create_shopping_list("Whole Foods", "organic apples", "quinoa", "almond milk", "kale", "avocados")
Real-World Example: Statistics Calculator
Let’s create a function that calculates statistics for any number of test scores:
def calculate_stats(student_name, *scores):
"""Calculate statistics for a student's test scores"""
if not scores:
return f"{student_name} has no scores recorded yet."
total_scores = len(scores)
total_points = sum(scores)
average = total_points / total_scores
highest = max(scores)
lowest = min(scores)
result = f"""
Student: {student_name}
Number of tests: {total_scores}
Scores: {', '.join(map(str, scores))}
Average: {average:.1f}
Highest: {highest}
Lowest: {lowest}
"""
return result.strip()
# Different students with different numbers of tests
print(calculate_stats("Alice", 85, 92, 78, 96))
print("\n" + "="*40 + "\n")
print(calculate_stats("Bob", 90, 88))
print("\n" + "="*40 + "\n")
print(calculate_stats("Charlie"))
When to Use *args
- When you don’t know how many arguments users will pass
- For mathematical functions (sum, average, max, min)
- When building flexible APIs
- For functions that work with collections of similar items
Keyword Variable Arguments (**kwargs) – Ultimate Flexibility
If *args is like a magic bag for regular arguments, then **kwargs is like a magic filing cabinet for labeled arguments. The “kwargs” stands for “keyword arguments,” and it can handle any number of named parameters you throw at it!
Imagine you’re ordering a custom pizza and you can specify any topping you want: extra_cheese=True, pepperoni=False, mushrooms=True, pineapple=True (yes, some people like it!). **kwargs works exactly like that.

Basic **kwargs Example
def create_user_profile(**details):
"""Create a user profile with any details provided"""
print("User Profile:")
print("-" * 20)
if not details:
print("No details provided!")
else:
for key, value in details.items():
print(f"{key.replace('_', ' ').title()}: {value}")
# Create different profiles with different information
create_user_profile(name="Alice", age=25, city="New York")
print()
create_user_profile(username="bob123", email="bob@email.com", favorite_color="blue", pet_name="Max")
print()
create_user_profile(first_name="Charlie", last_name="Brown", occupation="Teacher", hobbies="Reading, Swimming", phone="555-0123")
What’s Inside **kwargs?
Let’s see what **kwargs actually contains:
def examine_kwargs(**kwargs):
print(f"Type of kwargs: {type(kwargs)}")
print(f"Kwargs contains: {kwargs}")
print(f"Number of keyword arguments: {len(kwargs)}")
for key, value in kwargs.items():
print(f" {key} = {value} (type: {type(value).__name__})")
examine_kwargs(name="Diana", age=30, is_student=True, grades=[85, 92, 78])
**kwargs creates a dictionary where the keys are the parameter names and the values are what you passed in!
Combining Regular Arguments with **kwargs
def configure_database(database_name, **settings):
"""Configure a database with custom settings"""
print(f"Configuring database: {database_name}")
print("Settings:")
# Default settings
default_settings = {
'host': 'localhost',
'port': 5432,
'timeout': 30,
'auto_commit': True
}
# Update defaults with provided settings
default_settings.update(settings)
for setting, value in default_settings.items():
print(f" {setting}: {value}")
return default_settings
# Basic configuration with defaults
print("=== Basic Setup ===")
config1 = configure_database("user_data")
print()
# Custom configuration
print("=== Custom Setup ===")
config2 = configure_database(
"inventory",
host="192.168.1.100",
port=3306,
timeout=60,
ssl_enabled=True,
backup_enabled=True
)
Real-World Example: Pizza Ordering System
Let’s build a pizza ordering system that can handle any combination of toppings:
def order_pizza(size, crust="regular", **toppings):
"""Order a pizza with any toppings you want"""
base_prices = {'small': 8.99, 'medium': 12.99, 'large': 16.99}
topping_prices = {
'cheese': 1.50, 'pepperoni': 2.00, 'mushrooms': 1.75,
'olives': 1.25, 'pineapple': 1.50, 'bacon': 2.50
}
print(f"PIZZA ORDER")
print(f"Size: {size.title()}")
print(f"Crust: {crust.title()}")
total_price = base_prices.get(size.lower(), 12.99)
if toppings:
print("Toppings:")
for topping, quantity in toppings.items():
if quantity: # Only add if quantity is not 0 or False
topping_name = topping.replace('_', ' ').title()
if isinstance(quantity, bool) and quantity:
print(f" - {topping_name}")
total_price += topping_prices.get(topping, 1.00)
elif isinstance(quantity, int) and quantity > 0:
print(f" - {topping_name} (x{quantity})")
total_price += topping_prices.get(topping, 1.00) * quantity
else:
print("No extra toppings (plain pizza)")
print(f"\\nTotal Price: ${total_price:.2f}")
return total_price
# Different pizza orders
order_pizza("medium", pepperoni=True, cheese=True, mushrooms=True)
print("\\n" + "="*40 + "\\n")
order_pizza("large", "thin", bacon=2, pineapple=True, olives=False)
print("\\n" + "="*40 + "\\n")
order_pizza("small")
**kwargs is especially useful when you’re building APIs or working with configuration files. It’s similar to how building web applications with Flask often requires flexible parameter handling.
Combining All Types – The Complete Picture
Now comes the exciting part! You can combine all these different argument types in a single function. But there’s a specific order you must follow – it’s like getting dressed in the morning. You wouldn’t put your shoes on before your socks, right?
The Sacred Order
When combining different argument types, you must follow this exact order:
2. *args (variable positional arguments)
3. Keyword arguments (with defaults)
4. **kwargs (variable keyword arguments)
The Ultimate Function Example
def ultimate_function(required_arg, *args, default_arg="default", **kwargs):
"""A function that demonstrates all argument types"""
print("=== ULTIMATE FUNCTION CALL ===")
print(f"Required argument: {required_arg}")
if args:
print(f"Extra positional arguments (*args): {args}")
else:
print("No extra positional arguments")
print(f"Default argument: {default_arg}")
if kwargs:
print("Extra keyword arguments (**kwargs):")
for key, value in kwargs.items():
print(f" {key}: {value}")
else:
print("No extra keyword arguments")
# Test 1: Just the required argument
print("TEST 1:")
ultimate_function("I'm required!")
print()
# Test 2: Required + extra positional
print("TEST 2:")
ultimate_function("Still required", "extra1", "extra2", 42)
print()
# Test 3: All types together
print("TEST 3:")
ultimate_function(
"Required value", # required_arg
"bonus1", "bonus2", # *args
default_arg="custom default", # keyword argument
name="Alice", # **kwargs
age=25, # **kwargs
city="New York" # **kwargs
)
This flexible approach to function parameters is what makes Python so powerful. When you understand how to properly handle errors in your functions, like in mastering file handling and error management, combining it with flexible arguments creates robust, professional code.
Advanced Techniques – Pro Tips
Now that you’ve mastered the basics, let’s dive into some advanced techniques that professional Python developers use. These are the secret weapons that separate beginners from experts!

Positional-Only and Keyword-Only Arguments
Python 3.8 introduced special syntax to force arguments to be positional-only or keyword-only. This gives you more control over how your functions are called.
def advanced_function(pos_only1, pos_only2, /, normal_arg, *, kw_only1, kw_only2):
"""
- pos_only1, pos_only2: Must be passed as positional arguments
- normal_arg: Can be positional or keyword
- kw_only1, kw_only2: Must be passed as keyword arguments
"""
return f"pos_only: {pos_only1}, {pos_only2} | normal: {normal_arg} | kw_only: {kw_only1}, {kw_only2}"
# This works - correct usage
result = advanced_function("a", "b", "c", kw_only1="d", kw_only2="e")
print("✅ Correct usage:")
print(result)
print()
# This also works - normal_arg as keyword
result2 = advanced_function("a", "b", normal_arg="c", kw_only1="d", kw_only2="e")
print("✅ Also correct:")
print(result2)
# These would cause errors:
# advanced_function(pos_only1="a", pos_only2="b", "c", kw_only1="d", kw_only2="e") # Error!
# advanced_function("a", "b", "c", "d", "e") # Error!
Function Argument Unpacking
You can unpack lists and dictionaries directly into function calls. This is super useful when you have data stored in collections!
def create_person(name, age, city, occupation="Student"):
return f"{name} is a {age}-year-old {occupation} from {city}"
# Data stored in different formats
person_data_list = ["Alice", 25, "New York"]
person_data_dict = {"name": "Bob", "age": 30, "city": "London", "occupation": "Engineer"}
# Unpacking list with *
print("From list:")
print(create_person(*person_data_list))
# Unpacking dictionary with **
print("\\nFrom dictionary:")
print(create_person(**person_data_dict))
# Mixed unpacking
extra_info = {"city": "Paris", "occupation": "Artist"}
print("\\nMixed approach:")
print(create_person("Charlie", 28, **extra_info))
Understanding these advanced concepts helps when working with Python’s built-in functions like the Python id() function and the Python type() function, which themselves use flexible argument patterns.
Professional Best Practices
- Use type hints: Add type annotations to make your functions clearer
- Document your functions: Use docstrings to explain parameters
- Validate arguments: Check that inputs make sense before processing
- Keep it simple: Don’t use every feature just because you can
- Be consistent: Stick to the same patterns throughout your codebase
Test Your Knowledge – Interactive Quiz
Time to put your knowledge to the test! Don’t worry if you don’t get them all right the first time – that’s how we learn!
Question 1: What’s the correct order for different argument types?
Question 2: What type of data structure does *args create inside a function?
Question 3: Which of these function calls is INVALID?
Question 4: What does **kwargs create inside a function?
Question 5: When would you use default arguments?
Want more practice? Check out our comprehensive Python coding quiz for additional challenges!
Common Questions Answered
Here are the questions that almost every Python learner asks about function arguments:
Absolutely! You can use both *args and **kwargs in the same function. Just remember the order: regular arguments, *args, keyword arguments with defaults, then **kwargs.
Example: def my_func(required, *args, default="value", **kwargs):
No! The magic is in the asterisks (* and **), not the names. You could use *numbers, *items, **options, **settings, etc. But “args” and “kwargs” are standard conventions that every Python developer recognizes, so it’s best to stick with them.
Nothing breaks! If no extra positional arguments are passed, *args becomes an empty tuple. If no extra keyword arguments are passed, **kwargs becomes an empty dictionary. Your function should handle these empty cases gracefully.
*args is a tuple, so you can’t modify it directly (tuples are immutable). However, **kwargs is a dictionary, so you can add, remove, or modify its contents. But be careful – changes to **kwargs won’t affect the original variables passed to your function.
Use positional arguments for values that are always required and have an obvious order (like coordinates: x, y). Use keyword arguments when you have many parameters, when the meaning isn’t obvious from position, or when you want to make some parameters optional.
Parameters are the variables in the function definition (like variables in a recipe). Arguments are the actual values you pass when calling the function (like the actual ingredients you use). So in def greet(name):
, “name” is a parameter. In greet("Alice")
, “Alice” is an argument.
Technically yes, but it’s usually a bad idea! Python evaluates default arguments once when the function is defined, not each time it’s called. This means if you use a list as a default and modify it, the changes persist between function calls. Use None
as default and create the list inside the function instead.
Python has a limit of 255 parameters per function. But in practice, if you need more than 5-10 parameters, you should probably reconsider your function design. Consider using a dictionary, a class, or breaking the function into smaller pieces.
Leave a Reply