Functions in Python | Python Course | Dataplexa

Functions in Python

A function is a named block of code that performs a specific task. Instead of writing the same logic in multiple places throughout your program, you write it once inside a function and simply call it by name whenever you need it.

Think of a function like a coffee machine. You press a button (call the function), the machine does its work, and you get coffee (the result). You do not need to understand every internal step — you just use it. And you can press the button as many times as you want.

Functions are the single most important concept you will learn as you move into intermediate Python. Every real program — web apps, data science scripts, automation tools, games — is built from functions. From this lesson onwards, every program you write will use them.

Why Functions Exist

  • Reusability — write the logic once, use it anywhere in the program without repeating code.
  • Readability — a well-named function makes code read almost like plain English.
  • Organisation — break a large program into small, focused, manageable pieces.
  • Easy to fix — if there is a bug, you fix it in one place, not in 10 different places across the file.
  • Testability — small functions are easy to test individually before wiring together into a full program.

Defining and Calling a Function

You define a function using the def keyword, followed by the function name, parentheses, and a colon. The code inside must be indented. To run the function, you call it by writing its name followed by parentheses.

# DEFINE — creates the blueprint (does not run yet)
def greet():
    print("Hello! Welcome to Python.")
    print("Functions make code reusable.")

# CALL — this actually runs the code inside
greet()

# Call the same function multiple times
greet()
greet()
Hello! Welcome to Python. Functions make code reusable. Hello! Welcome to Python. Functions make code reusable. Hello! Welcome to Python. Functions make code reusable.
  • The def greet(): line defines the function — Python remembers it but does not run it yet.
  • The function only runs when you call it with greet().
  • Calling it three times prints the output three times — this is the core power of functions: write once, run everywhere.

Functions with Parameters

A parameter is a variable placed inside the parentheses in the function definition. It lets you pass information into the function so it can work with different data each time. The actual value you pass when calling is called an argument.

# "name" is the parameter — a placeholder for whatever gets passed in
def greet_user(name):
    print(f"Hello, {name}! Good to see you.")

# "Priya", "Kiran", "Arjun" are the arguments — the actual values
greet_user("Priya")
greet_user("Kiran")
greet_user("Arjun")

# Function with two parameters
def add_numbers(a, b):
    print(f"{a} + {b} = {a + b}")

add_numbers(10, 5)
add_numbers(100, 250)
Hello, Priya! Good to see you. Hello, Kiran! Good to see you. Hello, Arjun! Good to see you. 10 + 5 = 15 100 + 250 = 350
  • Each time greet_user() is called, the argument replaces the parameter name — so each person gets their own personalised message.
  • With two parameters, the order matters — first argument goes to a, second to b.
  • Parameter = the placeholder in the definition. Argument = the real value you pass when calling.

Returning Values from a Function

Functions that only print are useful, but in real programs you usually want a function to calculate something and give the result back so you can use it elsewhere. The return statement sends a value back to wherever the function was called. Once Python hits return, it exits the function immediately.

def multiply(a, b):
    result = a * b
    return result          # sends the value back to the caller

# Store the returned value in a variable
answer = multiply(6, 7)
print("6 x 7 =", answer)

# Or use the returned value directly
print("3 x 9 =", multiply(3, 9))

# Real-world use: calculate final price after discount
def final_price(price, discount_percent):
    discount = price * discount_percent / 100
    return price - discount

cost = final_price(2000, 15)    # 15% off Rs.2000
print("Final price after 15% off:", cost)
6 x 7 = 42 3 x 9 = 27 Final price after 15% off: 1700.0
  • return result sends 42 back — the variable answer now holds that value.
  • You can also use the returned value directly inside print() without storing it first.
  • A function without a return statement automatically returns None.

Default Parameter Values

You can give a parameter a default value so the function still works even if that argument is not provided. If an argument is passed, it overrides the default. If not, the default is used automatically.

# "country" has a default value of "India"
def register_user(name, country="India"):
    print(f"Name: {name} | Country: {country}")

register_user("Priya", "USA")   # default overridden
register_user("Kiran")          # default used
register_user("Arjun", "Canada")
Name: Priya | Country: USA Name: Kiran | Country: India Name: Arjun | Country: Canada
  • When register_user("Kiran") is called with only one argument, Python uses "India" as the default for country.
  • Default parameters must always come after non-default ones — def f(a, b="x") is valid, def f(a="x", b) is not.
  • Default values are a great way to make functions flexible without requiring every caller to provide all details.

Keyword Arguments

Normally arguments are passed in the same order as the parameters. With keyword arguments, you can pass them in any order by specifying the parameter name. This makes function calls clearer, especially when there are many parameters.

def create_profile(name, age, city):
    print(f"Name: {name} | Age: {age} | City: {city}")

# Positional — order must match exactly
create_profile("Sneha", 25, "Mumbai")

# Keyword — order does not matter
create_profile(age=30, city="Delhi", name="Rahul")

# Mix: positional first, then keyword
create_profile("Meera", city="Pune", age=22)
Name: Sneha | Age: 25 | City: Mumbai Name: Rahul | Age: 30 | City: Delhi Name: Meera | Age: 22 | City: Pune
  • In the second call, arguments are in a completely different order but Python matches each one correctly by name.
  • Positional arguments must always come before keyword arguments in the same call.
  • Keyword arguments make code more readable — seeing city="Pune" in a call is instantly clear.

*args — Any Number of Positional Arguments

Sometimes you do not know in advance how many arguments will be passed. Using *args lets a function accept any number of positional arguments. Python collects all of them into a tuple inside the function.

def add_all(*numbers):
    total = 0
    for n in numbers:
        total += n
    return total

print(add_all(5, 10))
print(add_all(1, 2, 3, 4, 5))
print(add_all(100, 200, 300, 400))

# Another example — greet multiple people
def greet_all(*names):
    for name in names:
        print(f"Hello, {name}!")

greet_all("Priya", "Kiran", "Arjun")
15 15 1000 Hello, Priya! Hello, Kiran! Hello, Arjun!
  • *numbers collects all arguments into a tuple — add_all(1, 2, 3, 4, 5) gives numbers = (1, 2, 3, 4, 5) inside the function.
  • The loop then adds them up — this works whether you pass 2 or 20 arguments.
  • args is just a convention — you could write *values or *items, but *args is the universally recognised style.

**kwargs — Any Number of Keyword Arguments

**kwargs lets a function accept any number of keyword arguments. All the name-value pairs are collected into a dictionary inside the function. This is very useful for flexible, configurable functions.

def show_details(**info):
    for key, value in info.items():
        print(f"  {key}: {value}")

print("--- User 1 ---")
show_details(name="Priya", age=25, city="Mumbai")

print("--- User 2 ---")
show_details(name="Kiran", country="India", plan="Premium", active=True)
--- User 1 --- name: Priya age: 25 city: Mumbai --- User 2 --- name: Kiran country: India plan: Premium active: True
  • **info collects all keyword arguments into a dictionary — name="Priya", age=25 becomes {"name": "Priya", "age": 25} inside.
  • Both calls pass completely different keyword arguments — different keys, different counts. The function handles both perfectly.
  • **kwargs is used heavily in real frameworks like Django and Flask for handling flexible configuration and form data.

Returning Multiple Values

A Python function can return more than one value at once. The values are packed into a tuple and can be unpacked into separate variables on the calling line — something many other languages cannot do as cleanly.

def get_stats(numbers):
    total   = sum(numbers)
    average = total / len(numbers)
    highest = max(numbers)
    return total, average, highest    # returns a tuple of three

marks = [72, 85, 90, 60, 88]

# Unpack all three returned values at once
total, avg, top = get_stats(marks)

print("Total   :", total)
print("Average :", avg)
print("Highest :", top)
Total : 395 Average : 79.0 Highest : 90
  • return total, average, highest packs all three into a tuple and sends them back at once.
  • total, avg, top = get_stats(marks) unpacks the tuple into three separate variables in one line.
  • This is cleaner than writing three separate functions — the standard Python pattern for functions that produce multiple related results.

Variable Scope — Local vs Global

Scope means where in your code a variable can be accessed. A variable created inside a function is local — it only exists while the function runs. A variable created outside all functions is global — it is accessible anywhere in the file.

app_name = "Dataplexa"    # global — lives outside any function

def show_info():
    version = "2.0"           # local — only exists inside this function
    print("App    :", app_name)   # can access global ✓
    print("Version:", version)    # can access local ✓

show_info()

# Global still accessible here
print("Outside:", app_name)

# Local is NOT accessible outside the function
try:
    print(version)
except NameError as e:
    print("Error:", e)
App : Dataplexa Version: 2.0 Outside: Dataplexa Error: name 'version' is not defined
  • app_name is global — visible both inside and outside functions.
  • version is local to show_info() — once the function finishes, it no longer exists. Trying to access it outside raises a NameError.
  • Local variables are a safety feature — they cannot accidentally interfere with the rest of your program. Always prefer local variables inside functions.

Documenting Functions with Docstrings

A docstring is a short description placed as the first line inside a function, written in triple quotes. It explains what the function does, what it expects, and what it returns. Docstrings are professional best practice — tools like VS Code, Jupyter, and Python's help() all read and display them.

def calculate_tax(income, rate):
    """
    Calculates the tax amount for a given income and tax rate.
    income : the total income (float or int)
    rate   : the tax rate as a percentage (e.g. 20 for 20%)
    Returns: the tax amount as a float
    """
    return income * rate / 100

# Read the docstring
print(calculate_tax.__doc__)

# Use the function
tax = calculate_tax(50000, 20)
print("Tax owed:", tax)
Calculates the tax amount for a given income and tax rate. income : the total income (float or int) rate : the tax rate as a percentage (e.g. 20 for 20%) Returns: the tax amount as a float Tax owed: 10000.0
  • The triple-quoted string right after the def line is the docstring — Python stores it as function.__doc__.
  • Writing a docstring for any function others will use takes 30 seconds and saves hours of confusion.
  • Always document: what the function does, what each parameter expects, and what it returns.

Real World Example — Invoice Generator

This program uses multiple functions together — each doing exactly one job. This is the correct way to structure real programs and is called the single responsibility principle.

def calculate_subtotal(price, quantity):
    """Returns subtotal before tax."""
    return price * quantity

def calculate_tax(subtotal, rate=18):
    """Returns the tax amount. Default rate is 18%."""
    return subtotal * rate / 100

def calculate_total(subtotal, tax):
    """Returns the final total."""
    return subtotal + tax

def print_invoice(item, price, quantity):
    """Prints a full formatted invoice for an item."""
    subtotal = calculate_subtotal(price, quantity)
    tax      = calculate_tax(subtotal)
    total    = calculate_total(subtotal, tax)
    print(f"  Item     : {item}")
    print(f"  Price    : {price}")
    print(f"  Quantity : {quantity}")
    print(f"  Subtotal : {subtotal}")
    print(f"  Tax (18%): {tax}")
    print(f"  Total    : {total}")

print("===== INVOICE =====")
print_invoice("Wireless Mouse", 799, 3)
print("===================")
===== INVOICE ===== Item : Wireless Mouse Price : 799 Quantity : 3 Subtotal : 2397 Tax (18%): 431.46 Total : 2828.46 ===================
  • Each small function does exactly one calculation — easy to understand, test, and reuse.
  • print_invoice() calls the three helper functions — functions calling other functions is completely normal and very powerful.
  • If the tax rate ever changes, you update it in calculate_tax() only — one change, entire program updated. This is why functions save hours in real projects.

Quick Reference Table

ConceptSyntaxWhat It Does
Definedef greet():Creates a reusable block of code
Callgreet()Runs the code inside the function
Parametersdef greet(name):Accepts input values
Return valuereturn resultSends a value back to the caller
Default parameterdef f(x, y=10):Used when argument is not provided
Keyword argumentf(y=5, x=2)Pass arguments in any order by name
*argsdef f(*args):Any number of positional args → tuple
**kwargsdef f(**kwargs):Any number of keyword args → dict
Multiple returnreturn a, b, cReturns multiple values as a tuple
Local variablex = 5 (inside function)Only exists inside that function
Global variablex = 5 (outside function)Accessible everywhere in the file
Docstring"""description"""Documents what the function does

Practice

What keyword is used to define a function in Python?



Which keyword sends a value back from a function to the caller?



When you use *args, all extra positional arguments are collected into a __________.



What error is raised when you try to access a local variable outside its function?



When you use **kwargs, all keyword arguments are collected into a __________.



Quick Quiz

When does the code inside a function actually run?





What happens when you call a function without providing a value for a default parameter?





How do you return two values a and b from a function?





What is a local variable?





Which syntax collects any number of keyword arguments into a dictionary?





NEXT UP
Lambda Functions in Python
Learn how to write compact, single-expression functions in one line using Python's lambda syntax — perfect for quick transformations, sorting keys, and use with map and filter.