*args & **kwargs
Learn how to create flexible Python functions that can
accept a variable number of positional and keyword
arguments using *args and
**kwargs.
What You Will Learn
By the end of this lesson, you will understand how Python functions can accept a flexible number of arguments.
- Understand positional and keyword arguments.
-
Understand the purpose of
*args. -
Understand the purpose of
**kwargs. -
Understand why
*argsbecomes a tuple. -
Understand why
**kwargsbecomes a dictionary. - Use both techniques together.
-
Understand argument unpacking with
*and**.
What Is *args?
Normally, a function is defined with a fixed number of parameters.
def add(a, b):
return a + b
print(add(10, 20))
What if you want the function to accept 2, 3, 5, or even 100 values?
This is where *args becomes useful.
*args allows a function to accept
a variable number of positional arguments.
Why Use *args?
Consider a function that calculates a total. You may want to pass any number of prices.
def add_three(a, b, c):
return a + b + c
print(add_three(10, 20, 30))
This function only accepts exactly three values.
With *args, we can make the function much
more flexible.
def add_numbers(*args):
return sum(args)
print(add_numbers(10, 20))
print(add_numbers(10, 20, 30))
print(add_numbers(10, 20, 30, 40, 50))
30 60 150
*args Syntax
The basic syntax is:
def function_name(*args):
# use args here
pass
The name args is only a convention.
Python cares about the *, not the word
args.
def show_values(*values):
print(values)
show_values(10, 20, 30)
(10, 20, 30)
Still, *args is the widely recognized
naming convention and should normally be preferred.
*args Is a Tuple
Inside the function, all extra positional arguments are collected into a tuple.
def show_args(*args):
print(args)
print(type(args))
show_args("Python", "Java", "C++")
('Python', 'Java', 'C++')
<class 'tuple'>
*args collects extra positional
arguments into a tuple.
Looping Through *args
Because args is a tuple, you can loop
through it just like any other tuple.
def print_names(*args):
for name in args:
print(name)
print_names("Ali", "Sara", "Ahmed")
Ali Sara Ahmed
Indexing *args
Since args is a tuple, you can use
indexing and slicing.
def show_first(*args):
print("First:", args[0])
print("Last:", args[-1])
show_first("Python", "Java", "C++", "JavaScript")
First: Python Last: JavaScript
If the function is called without arguments,
args[0] will cause an
IndexError.
Practical *args Examples
Calculating an Average
def average(*numbers):
if not numbers:
return 0
return sum(numbers) / len(numbers)
print(average(10, 20, 30))
print(average(80, 90, 70, 100))
20.0 85.0
Building a Shopping Total
def shopping_total(*prices):
return sum(prices)
total = shopping_total(1200, 850, 500, 300)
print(f"Total: Rs. {total}")
Total: Rs. 2850
What Is **kwargs?
**kwargs allows a function to accept a
variable number of keyword arguments.
Keyword arguments are passed using names such as
name="Ali" or
age=25.
def show_profile(**kwargs):
print(kwargs)
show_profile(
name="Ali",
age=25,
city="Multan"
)
{'name': 'Ali', 'age': 25, 'city': 'Multan'}
**kwargs allows a function to
accept a variable number of keyword arguments.
Why Use **kwargs?
Imagine building a function that creates a user profile. Different users may have different pieces of information.
def create_profile(**details):
for key, value in details.items():
print(f"{key}: {value}")
create_profile(
name="Sara",
age=24,
profession="Designer",
city="Lahore"
)
name: Sara age: 24 profession: Designer city: Lahore
The caller can provide different named values without changing the function definition.
**kwargs Syntax
def function_name(**kwargs):
# use kwargs here
pass
Just like args, the name
kwargs is a convention. The important
part is **.
def show_data(**data):
print(data)
show_data(name="Ali", age=30)
{'name': 'Ali', 'age': 30}
**kwargs Is a Dictionary
Inside the function, keyword arguments are collected into a dictionary.
def show_data(**kwargs):
print(kwargs)
print(type(kwargs))
show_data(name="Ali", age=25)
{'name': 'Ali', 'age': 25}
<class 'dict'>
Therefore, you can use dictionary methods such as
.keys(), .values(), and
.items().
Looping Through **kwargs
def display_info(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
display_info(
name="Ahmed",
age=28,
city="Islamabad"
)
name: Ahmed age: 28 city: Islamabad
*args vs **kwargs
*args
Collects extra positional arguments into a tuple.
**kwargs
Collects extra keyword arguments into a dictionary.
def example(*args, **kwargs):
print("args:", args)
print("kwargs:", kwargs)
example(
10,
20,
30,
name="Ali",
city="Multan"
)
args: (10, 20, 30)
kwargs: {'name': 'Ali', 'city': 'Multan'}
Using *args and **kwargs Together
A function can accept both types at the same time.
def user_data(*args, **kwargs):
print("Values:")
for value in args:
print(value)
print("Details:")
for key, value in kwargs.items():
print(f"{key}: {value}")
user_data(
"Python",
"Programming",
level="Beginner",
duration="3 months"
)
Values: Python Programming Details: level: Beginner duration: 3 months
Normal Parameters with *args and **kwargs
You can combine normal parameters with variable arguments.
def student(name, *marks):
print("Student:", name)
print("Marks:", marks)
student("Ali", 80, 75, 90)
Student: Ali Marks: (80, 75, 90)
Here, name receives the first argument,
while the remaining positional arguments are collected
by marks.
Argument Unpacking
The * and ** operators are
also used when calling functions to unpack collections.
Unpacking a List
def add(a, b, c):
return a + b + c
numbers = [10, 20, 30]
print(add(*numbers))
60
*numbers unpacks the list into separate
positional arguments.
Unpacking a Dictionary
def introduce(name, age):
print(f"My name is {name} and I am {age} years old.")
person = {
"name": "Sara",
"age": 24
}
introduce(**person)
My name is Sara and I am 24 years old.
In a function definition,
*args and **kwargs
collect arguments. During a function call,
* and ** can unpack
collections.
Real-World Examples
Example 1 — Product Price Calculator
def calculate_cart(*prices, discount=0):
subtotal = sum(prices)
discount_amount = subtotal * discount
final_total = subtotal - discount_amount
return final_total
total = calculate_cart(
1200,
800,
500,
discount=0.10
)
print(f"Final Total: Rs. {total:.2f}")
Final Total: Rs. 2250.00
Example 2 — Flexible User Registration
def register_user(username, **details):
print("Username:", username)
for key, value in details.items():
print(f"{key}: {value}")
register_user(
"adnan123",
email="user@example.com",
city="Multan",
role="Student"
)
Username: adnan123 email: user@example.com city: Multan role: Student
Example 3 — Flexible Report Generator
def create_report(*values, **settings):
print("Values:", values)
print("Settings:")
for key, value in settings.items():
print(f"{key}: {value}")
create_report(
100,
200,
300,
title="Sales Report",
format="PDF",
year=2026
)
Values: (100, 200, 300) Settings: title: Sales Report format: PDF year: 2026
Common Mistakes
Mistake 1 — Confusing args with kwargs
*args handles extra positional
arguments, while **kwargs handles
extra keyword arguments.
Mistake 2 — Forgetting the Star
def add(args):
return sum(args)
This expects one argument, such as a list or tuple. It does not accept an arbitrary number of positional arguments.
For variable positional arguments, use:
def add(*args):
return sum(args)
Mistake 3 — Accessing an Empty args Tuple
def first_value(*args):
return args[0]
print(first_value())
This causes an IndexError because there
is no first item.
Mistake 4 — Treating kwargs Like a Tuple
kwargs is a dictionary, so use dictionary
operations such as .items() and
.get().
Mistake 5 — Thinking args and kwargs Are Special Names
The names args and kwargs are
conventions. The operators * and
** are what give them their behavior.
Practice Exercises
Try solving each exercise yourself before checking a solution.
Exercise 1 — Sum Numbers
Create a function called total() that
accepts any number of numbers using
*args and returns their sum.
Exercise 2 — Find Maximum
Create a function that accepts any number of numeric arguments and returns the largest value.
Exercise 3 — Print Names
Create a function that accepts any number of names and prints each name on a separate line.
Exercise 4 — User Details
Create a function called
show_user() that accepts any number
of keyword arguments and displays them.
Exercise 5 — Product Information
Use **kwargs to create a product
containing fields such as name, price, category,
and stock.
Exercise 6 — Both Together
Create a function using both *args and
**kwargs. Print each group separately.
Exercise 7 — Argument Unpacking
Create a list of three numbers and pass it to a
three-parameter function using *.
Challenge — Flexible Sales Report
Build a flexible sales-report function that can accept any number of sales values and optional report settings.
Your Task
Create a function named
sales_report().
It should:
-
Accept any number of sales amounts using
*args. - Calculate the total sales.
- Calculate the average sale.
- Find the highest sale.
-
Accept optional settings using
**kwargs. - Display those settings in the report.
sales_report(
12000,
8500,
15000,
9200,
title="Monthly Sales",
month="August",
currency="PKR"
)
Use args for the collection of
sales numbers and kwargs for
flexible report configuration.
Quick Quiz
What does *args collect?
What type is args inside the
function?
What does **kwargs collect?
What type is kwargs inside the
function?
Which operator is used to unpack a list into positional arguments?
Which operator unpacks a dictionary into keyword arguments?
Are args and kwargs
special Python keywords?
Can a function use both *args and
**kwargs?
Which is normally used for optional named configuration values?
What does *numbers do when calling
a function?
Lesson Summary
*args and **kwargs allow
Python functions to accept flexible numbers of
arguments.
-
*argscollects extra positional arguments. -
argsis stored as a tuple. -
**kwargscollects extra keyword arguments. -
kwargsis stored as a dictionary. -
*argsis useful when the number of positional values is unknown. -
**kwargsis useful when the number of named options is unknown. - Both can be used in the same function.
-
*can unpack lists, tuples, and other iterables into positional arguments. -
**can unpack dictionaries into keyword arguments.
Think of *args as
many positional values and
**kwargs as
many named values. Once this
distinction becomes natural, flexible Python
functions become much easier to design.
You've finished *args & **kwargs!
Mark this lesson complete when you are comfortable working with variable positional arguments, keyword arguments, and argument unpacking.