Arguments
Learn the different ways to pass data into functions in Python — positional, keyword, default, *args, and **kwargs.
Arguments
When you call a function, you pass in data for it to work with. Python gives you several different ways to do this — each one useful in different situations. Understanding them well makes your functions flexible, readable, and powerful.
Positional arguments
The simplest kind. Values are matched to parameters by their position — first argument goes to the first parameter, second to the second, and so on.
def introduce(name, age, city):
print(f"My name is {name}, I am {age}, from {city}.")
introduce("Ali", 22, "Lahore")
# My name is Ali, I am 22, from Lahore.Order matters. If you swap them, the values go to the wrong parameters:
introduce(22, "Lahore", "Ali")
# My name is 22, I am Lahore, from Ali.Keyword arguments
Instead of relying on position, you name the parameter explicitly when calling the function:
introduce(name="Ali", age=22, city="Lahore")
# My name is Ali, I am 22, from Lahore.Because you are naming them, order no longer matters:
introduce(city="Lahore", name="Ali", age=22)
# My name is Ali, I am 22, from Lahore.You can also mix positional and keyword — but positional arguments must always come first:
introduce("Ali", city="Lahore", age=22) # fine
introduce(name="Ali", 22, "Lahore") # SyntaxError — keyword before positionalKeyword arguments make function calls much easier to read, especially when a function has many parameters. create_user("Ali", True, False, 3) tells you nothing — create_user("Ali", is_admin=True, is_banned=False, role=3) tells you everything.
Default arguments
You can give a parameter a default value. If the caller does not pass that argument, the default is used.
def greet(name, message="Hello"):
print(f"{message}, {name}!")
greet("Ali") # Hello, Ali!
greet("Sara", "Good morning") # Good morning, Sara!Default parameters must always come after non-default ones:
def greet(message="Hello", name): # SyntaxError
pass
def greet(name, message="Hello"): # correct
passA practical example — a function that creates a user profile with sensible defaults:
def create_user(username, role="viewer", is_active=True):
print(f"User: {username} | Role: {role} | Active: {is_active}")
create_user("ali")
# User: ali | Role: viewer | Active: True
create_user("sara", role="admin")
# User: sara | Role: admin | Active: True
create_user("omar", role="editor", is_active=False)
# User: omar | Role: editor | Active: FalseNever use a mutable object like a list or dictionary as a default value. This is a well-known Python trap:
# Wrong — the list is shared across all calls
def add_item(item, cart=[]):
cart.append(item)
return cart
print(add_item("apple")) # ['apple']
print(add_item("banana")) # ['apple', 'banana'] — not what you expectedUse None as the default and create the object inside the function instead:
# Correct
def add_item(item, cart=None):
if cart is None:
cart = []
cart.append(item)
return cart*args — variable positional arguments
What if you want a function to accept any number of positional arguments? Use *args. Python collects all extra positional arguments into a tuple.
def add(*args):
print(args)
return sum(args)
print(add(1, 2)) # (1, 2) → 3
print(add(1, 2, 3, 4, 5)) # (1, 2, 3, 4, 5) → 15The name args is just a convention — the * is what matters. You could write *numbers and it would work the same way.
def greet(*names):
for name in names:
print(f"Hello, {name}!")
greet("Ali", "Sara", "Omar")
# Hello, Ali!
# Hello, Sara!
# Hello, Omar!You can combine regular parameters with *args — but *args must come after regular parameters:
def introduce(greeting, *names):
for name in names:
print(f"{greeting}, {name}!")
introduce("Hi", "Ali", "Sara", "Omar")
# Hi, Ali!
# Hi, Sara!
# Hi, Omar!**kwargs — variable keyword arguments
**kwargs does the same thing but for keyword arguments. Python collects all extra keyword arguments into a dictionary.
def show_info(**kwargs):
print(kwargs)
show_info(name="Ali", age=22, city="Lahore")
# {'name': 'Ali', 'age': 22, 'city': 'Lahore'}You can loop over it like any dictionary:
def display_profile(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
display_profile(name="Ali", age=22, city="Lahore", role="admin")Output:
name: Ali
age: 22
city: Lahore
role: adminA practical example — a flexible function that builds an HTML tag:
def build_tag(tag, content, **attributes):
attrs = ""
for key, value in attributes.items():
attrs += f' {key}="{value}"'
return f"<{tag}{attrs}>{content}</{tag}>"
print(build_tag("a", "Click here", href="https://example.com", target="_blank"))
# <a href="https://example.com" target="_blank">Click here</a>
print(build_tag("p", "Hello world", class_="intro", id="main"))
# <p class_="intro" id="main">Hello world</p>The correct order
When you combine all argument types in one function, they must follow this order:
def function(positional, default=value, *args, **kwargs):def example(name, role="viewer", *args, **kwargs):
print(f"name: {name}")
print(f"role: {role}")
print(f"args: {args}")
print(f"kwargs: {kwargs}")
example("Ali", "admin", "extra1", "extra2", city="Lahore", age=22)Output:
name: Ali
role: admin
args: ('extra1', 'extra2')
kwargs: {'city': 'Lahore', 'age': 22}Unpacking arguments with * and **
You can also go the other way — unpack a list or dictionary into a function call:
def add(a, b, c):
return a + b + c
numbers = [1, 2, 3]
print(add(*numbers)) # 6 — unpacks the list into positional args
data = {"a": 1, "b": 2, "c": 3}
print(add(**data)) # 6 — unpacks the dict into keyword argsSummary
| Type | Syntax | What it does |
|---|---|---|
| Positional | func(1, 2) | Matched by order |
| Keyword | func(a=1, b=2) | Matched by name |
| Default | def func(a=10) | Used when not passed |
*args | def func(*args) | Collects extra positional into a tuple |
**kwargs | def func(**kwargs) | Collects extra keyword into a dict |
| Unpack list | func(*list) | Spreads list into positional args |
| Unpack dict | func(**dict) | Spreads dict into keyword args |