Python Functions: def, Arguments, Return and *args - Python with AI tutorial chapter 20
Python

Python Functions: def, Arguments, Return and *args

Python with AI Tutorial · Chapter 20 of 48

A Python function is a named, reusable block of code that performs one task. You define it once with the def keyword, give it inputs called parameters, and call it as many times as you need. Functions return results with return, accept optional and keyword arguments, and can take any number of values through *args and **kwargs.

Defining and calling a function

Start with def, the function name, parentheses and a colon. The indented body runs only when the function is called. Defining a function does nothing by itself; calling it with parentheses executes the code.

def print_header():
    print("=" * 30)
    print("Monthly Sales Report")
    print("=" * 30)

print_header()
Output:
==============================
Monthly Sales Report
==============================

Function names follow the same rules as variables: lowercase words separated by underscores. Choose a verb that says what the function does, such as calculate_tax or send_invoice, so the call reads like a sentence.

Parameters and arguments

Parameters are the names listed in the definition. Arguments are the actual values you pass when calling. Inside the function the parameter behaves like a local variable holding that value.

def greet_customer(name, order_id):
    print(f"Hello {name}, your order {order_id} is confirmed.")

greet_customer("Meera", 5021)
greet_customer("Jon", 5022)
Output:
Hello Meera, your order 5021 is confirmed.
Hello Jon, your order 5022 is confirmed.

Calling with the wrong number of arguments raises a TypeError. Python tells you exactly which parameter is missing, which makes these errors quick to fix.

Returning a value

return sends a result back to the caller and ends the function immediately. A function without a return statement returns None. You can return several values separated by commas; Python packs them into a tuple.

def invoice_total(subtotal, tax_rate=0.18):
    tax = subtotal * tax_rate
    return subtotal + tax, tax

total, tax = invoice_total(2500)
print(f"Total {total:.2f} (tax {tax:.2f})")
print(invoice_total(1000, 0.05))
Output:
Total 2950.00 (tax 450.00)
(1050.0, 50.0)

Returning values is better than printing inside the function because the caller decides what to do with the result: display it, store it in a list, or feed it into another calculation.

Kinds of arguments

Python offers several ways to pass arguments. The table summarises them, and the sections below show each in action.

Kind Syntax in definition Example call Notes
Positional def f(a, b) f(1, 2) Matched by order
Keyword def f(a, b) f(b=2, a=1) Matched by name, any order
Default def f(a, b=10) f(1) Optional; uses 10 if omitted
Variable positional def f(*args) f(1, 2, 3) Collected into a tuple
Variable keyword def f(**kwargs) f(x=1, y=2) Collected into a dict

Keyword and default arguments

Keyword arguments name the parameter at the call site, which makes long calls readable and lets you skip optional parameters. Default values must come after any parameters without defaults.

def apply_discount(price, percent=10, min_price=0):
    discounted = price * (1 - percent / 100)
    return max(discounted, min_price)

print(apply_discount(200))
print(apply_discount(200, percent=25))
print(apply_discount(price=50, min_price=45, percent=20))
Output:
180.0
150.0
45

Notice the last call: 50 less 20% is 40, but the min_price floor of 45 wins. Keyword arguments let you set min_price without repeating the default for percent.

Common mistake: never use a mutable default such as def add_item(item, basket=[]). The same list is shared by every call, so items pile up between calls. Use basket=None and create the list inside the function when it is None.

*args: any number of positional arguments

Prefix a parameter with a single asterisk to collect all remaining positional arguments into a tuple. This is how print() and max() accept any number of values.

def total_sales(*amounts):
    print(f"Received {len(amounts)} figures")
    return sum(amounts)

print(total_sales(1200, 850, 430))
print(total_sales())
Output:
Received 3 figures
2480
Received 0 figures
0

The name args is only a convention; *amounts is clearer here. You can also unpack a list into a call with the same asterisk: total_sales(*monthly_list).

**kwargs: any number of keyword arguments

Two asterisks collect extra keyword arguments into a dictionary. It is handy for functions that build records or pass settings through to another function.

def build_employee(name, **details):
    record = {"name": name}
    record.update(details)
    return record

emp = build_employee("Ravi", department="Finance", grade="L3", remote=True)
print(emp)
print(emp["department"])
Output:
{'name': 'Ravi', 'department': 'Finance', 'grade': 'L3', 'remote': True}
Finance
Try it with AI

Ask the assistant to design a function signature for a real task and to justify which parameters should be positional, keyword-only or defaulted.

Design a Python function called generate_payslip that needs an employee name, basic salary, optional allowances, an optional tax rate defaulting to 0.1, and any number of extra deductions passed by name. Write the def line with sensible defaults, use *args or **kwargs where appropriate, add a docstring, and show three example calls with their printed output.

Docstrings and type hints

A docstring is a string on the first line of the body that explains what the function does. Type hints describe the expected types of parameters and the return value. Neither changes how Python runs the code, but both help editors, AI assistants and colleagues understand your intent.

def days_overdue(due_day: int, today: int) -> int:
    """Return how many days an invoice is overdue, never negative."""
    return max(0, today - due_day)

print(days_overdue(10, 17))
print(days_overdue(20, 17))
print(days_overdue.__doc__)
Output:
7
0
Return how many days an invoice is overdue, never negative.

Functions calling functions

Small functions combine into larger ones. Each piece is easy to test on its own, and a bug is easy to localise because every function has a single job.

def net_pay(gross):
    return gross - income_tax(gross)

def income_tax(gross):
    if gross <= 3000:
        return 0
    return (gross - 3000) * 0.2

for salary in (2500, 4000, 6500):
    print(salary, "->", net_pay(salary))
Output:
2500 -> 2500
4000 -> 3800.0
6500 -> 5800.0

net_pay refers to income_tax before that function is defined in the file. This works because the name is only looked up when net_pay is actually called, by which time both definitions have run.

Try it with AI

Paste a long script and ask the assistant to break it into functions, then check that the refactored version still produces the same output.

Here is a 40-line Python script that reads a list of orders, calculates totals with tax, applies a bulk discount above 10 items and prints a summary. Refactor it into three or four small functions with clear names, docstrings and type hints. Keep the printed output identical and explain what each function is responsible for.

Common mistakes

  • Forgetting the parentheses when calling, so print_header refers to the function object instead of running it.
  • Printing a result instead of returning it, which makes the value impossible to reuse.
  • Placing a parameter with a default before one without, which is a SyntaxError.
  • Using a mutable default argument such as an empty list or dictionary.
  • Writing code after return inside the same block; it never runs.

Exercise

Write a function commission(sales, rate=0.05, *bonuses) that returns sales multiplied by the rate plus the sum of any bonus amounts passed after it. Call it three ways: with sales only, with a custom rate, and with a rate plus two bonuses.

Show answer
def commission(sales, rate=0.05, *bonuses):
    return sales * rate + sum(bonuses)

print(commission(40000))
print(commission(40000, 0.08))
print(commission(40000, 0.08, 500, 250))
Output:
2000.0
3200.0
3950.0

Related chapters

FAQ

What is the difference between a parameter and an argument in Python?

A parameter is the name in the function definition, such as price in def f(price). An argument is the actual value passed when calling, such as f(200).

What does *args mean in a Python function?

*args collects any extra positional arguments into a tuple, so the function can accept one value or fifty. **kwargs does the same for keyword arguments, collecting them into a dictionary.

What does a Python function return if there is no return statement?

It returns None. If you print the result of such a function you will see None, which is a common sign that a return line is missing.

Working with spreadsheets too? Ready-made Excel, Google Sheets and Power BI templates are at NextGenTemplates.com.

Chapter 20 of 48 · Python with AI: all 48 chapters

PK
Meet PK, the founder of NeotechNavigators.com! With over 15 years of experience in Data Visualization, Excel Automation, and dashboard creation. PK is a Microsoft Certified Professional who has a passion for all things in Excel. PK loves to explore new and innovative ways to use Excel and is always eager to share his knowledge with others. With an eye for detail and a commitment to excellence, PK has become a go-to expert in the world of Excel. Whether you're looking to create stunning visualizations or streamline your workflow with automation, PK has the skills and expertise to help you succeed. Join the many satisfied clients who have benefited from PK's services and see how he can take your data analysis skills to the next level!
https://neotechnavigators.com