A function is a reusable block of code that performs a specific task. Instead of copying the same logic every time you need it, you define it once with def and call it by name whenever you need it — which is the single biggest habit that separates clean code from repetitive, hard-to-maintain code.

Python functions are also first-class values: you can pass them around, store them in variables, and return them from other functions, which is what makes features like lambda functions and decorators possible later on.

Defining and Calling a Function

def greet(name):
    """Return a greeting for the given name."""
    return f"Hello, {name}!"

message = greet("Asha")
print(message)

Output:

Hello, Asha!

Here, name is a parameter — a placeholder defined in the function signature. "Asha" is the argument — the actual value passed in when the function is called. The two words are often used loosely, but this is the technical distinction between them.

Return Values vs. No Return

A function doesn't have to return anything. If there's no return statement — or a bare return with no value — Python hands back None automatically.

def log_message(text):
    print(f"[LOG] {text}")
    # no return statement here

result = log_message("Server started")
print(result)

Output:

[LOG] Server started
None

This trips up a lot of beginners — log_message() prints something to the screen, but that's not the same as returning a value you can store and use later. Only an explicit return sends a value back to the caller.

Multiple Parameters and Multiple Return Values

def min_and_max(numbers):
    return min(numbers), max(numbers)

low, high = min_and_max([4, 9, 1, 7, 3])
print(f"Smallest: {low}, Largest: {high}")

Output:

Smallest: 1, Largest: 7

Python doesn't actually support "multiple return values" in a literal sense — return min(numbers), max(numbers) packs both values into a tuple, which is then unpacked into low and high on the calling side.

Where to Go Next

Once you're comfortable with the basics here, the next steps are default and keyword arguments, *args and **kwargs for functions that accept a variable number of arguments, and variable scope, which explains exactly which variables a function can see and modify.