ASAmol Shukla
Projects
Courses
Prompts
Skills
Contact
Resume
Course Outline
Syllabus Overview

Complete Python Course: From Zero to Professional

Courses/Complete Python Course: From Zero to Professional/Lesson 12: Functions — Reusable Building Blocks
45 mins lesson duration•9 mins read

Lesson 12: Functions — Reusable Building Blocks

Define functions, parameters, default arguments, return values, and docstrings.

Why Functions?

A function is a named block of reusable code. Write it once, call it many times. Functions are how you stop repeating yourself (the DRY principle: Don't Repeat Yourself) and keep programs readable. If you find yourself copying and pasting the same 5 lines, that code belongs in a function.

Functions are the single most important organizational tool in programming. Every real program — from a web server to a data pipeline — is a collection of functions working together. This lesson gives you the complete mental model: how to write them, call them, and reason about them.

What You'll Learn in This Lesson

  • Define functions with def
  • Pass parameters and use default arguments
  • Return values — and understand None
  • Write docstrings that document your code
  • Understand local vs global scope

Function Anatomy

def greet(name, excited=True):
    """Return a friendly greeting."""
    message = f"Hello, {name}!"
    if excited:
        message += " 🎉"
    return message
Part Purpose
def Keyword that defines a function
greet Function name (snake_case)
name, excited=True Parameters (inputs)
: Ends the header — body must be indented
"""...""" Docstring — documentation
return message Hands a value back to the caller

Call it like this:

print(greet("Amol"))          # Hello, Amol! 🎉  (uses default)
print(greet("Riya", False))   # Hello, Riya!
print(greet(name="Sam"))      # keyword argument — self-documenting

Mental model: a function is a machine. Inputs go in the funnel (parameters), work happens inside, and the result rolls out (return). Trace each call separately — a fresh set of local boxes every time.


Parameters vs Arguments

  • Parameters are the names in the function definition.
  • Arguments are the values you pass when calling.
Calling style Example Notes
Positional area(5, 4) Order matters
Keyword area(length=5, width=4) Order-free, self-documenting
Default discount(200) Omits optional params
def area(length, width):
    return length * width

area(5, 4)                  # positional
area(width=4, length=5)     # keyword — order doesn't matter

Which style should you use? Positional for short, obvious calls (area(5, 4)); keyword for clarity when a call has several arguments or the meaning isn't obvious. Professionals mix both: send_email("riya@example.com", subject="Hello", urgent=True).


Default Arguments

Parameters can have default values, making them optional:

def discount(price, percent=10):
    return price * (1 - percent / 100)

print(discount(200))       # 180.0  (uses 10%)
print(discount(200, 50))   # 100.0  (overrides to 50%)

Rules:

  • Defaults come after required parameters: def f(a, b=1): ✅, def f(a=1, b): ❌
  • Defaults are evaluated once at definition time — never use a mutable default like def f(items=[]) (see the mistakes section).

The mutable-default trap in detail:

def add_item(item, cart=[]):      # BAD — cart is shared across calls!
    cart.append(item)
    return cart

print(add_item("apple"))    # ['apple']
print(add_item("banana"))   # ['apple', 'banana'] — the first call's list leaked in!

The default [] is created once when the function is defined, then reused on every call. The fix is the None sentinel pattern:

def add_item(item, cart=None):    # GOOD
    if cart is None:
        cart = []
    cart.append(item)
    return cart

return — and the Mystery of None

return hands a value back to the caller and exits the function immediately:

def shout(text):
    print(text.upper())   # prints, but returns NOTHING

result = shout("hello")   # HELLO
print(result)             # None

Every function returns something. If you don't write return, Python returns None automatically. None is a special value meaning "nothing here".

Behavior Returns
Has return value That value
Has bare return None
No return statement None

print() vs return — the eternal beginner question: print() shows a value on the screen; return hands a value to the caller. A function that only prints cannot be used in further calculations. A function that returns can: double = area(5, 4) * 2. When in doubt, return — you can always print at the call site.


Docstrings — Documentation Built In

A triple-quoted string right after the def line documents what the function does:

def calculate_grade(score, bonus=0):
    """Return a letter grade for a score plus optional bonus."""
    ...

Professional teams treat docstrings as non-negotiable. Tools like help(), IDEs, and documentation generators read them automatically.

Writing a good docstring — the three W's:

  1. What it does (one sentence, imperative: "Return...", "Compute...", "Validate...").
  2. What it takes (parameters) and returns, when it isn't obvious.
  3. What can go wrong (exceptions raised).
def withdraw(balance, amount):
    """Subtract amount from balance.

    Args:
        balance: Current account balance (float).
        amount: Amount to withdraw (float).

    Returns:
        New balance after withdrawal.

    Raises:
        ValueError: If amount exceeds balance.
    """

Scope — Where Names Live

Variables created inside a function stay inside it (local scope):

language = "Python"       # global scope

def show():
    print(language)       # can READ globals ✅
    greeting = "Hi"       # local to show() — dies when show() ends
  • A function can read global variables.
  • It cannot change them without the global keyword (Lesson 14).
  • Each call creates a fresh set of local boxes — no leftovers between calls.

Why scope is a feature, not a limitation: because functions can't accidentally overwrite your global variables, large programs stay safe. Each function is an isolated universe — that isolation is what makes code predictable and testable.


Common Mistakes to Avoid

  • Mistake: def f(items=[]) — mutable default shared across calls — Fix: use def f(items=None): items = [] if items is None else items.
  • Mistake: Forgetting return and getting None back — Fix: remember: no return = None.
  • Mistake: Calling a function before defining it in the file — Fix: definitions run top-to-bottom; define before you call (or put calls in a main()).
  • Mistake: Using print() when you need the value for further math — Fix: return the value.
  • Mistake: Naming a function with an uppercase letter or spaces — Fix: snake_case: calculate_grade, not CalculateGrade.

Professional Tips & Tricks

  • One function = one job. If a function does three things, split it.
  • Use keyword arguments at call sites: calculate_grade(score=88, bonus=5).
  • Write the docstring FIRST — it forces you to think about what the function should do.
  • Prefer return over print inside functions — returned values are reusable.
  • Keep functions short (roughly under 20 lines); long functions are a signal to split.

Key Takeaways

  • def name(params): defines a function; the body must be indented.
  • Defaults make parameters optional; defaults go last.
  • Every function returns a value — explicit return or None.
  • Docstrings document functions and are read by help() and IDEs.
  • Local scope keeps function variables isolated.
  • Never use mutable defaults; use the None sentinel pattern.

Next up: Lambda, *args & **kwargs — flexible and anonymous functions.

Interactive Lesson Code Snippet
# Defining and calling functions
def calculate_grade(score, bonus=0):
    """Return a letter grade for a score plus optional bonus."""
    total = score + bonus
    if total >= 90:
        return "A"
    if total >= 75:
        return "B"
    if total >= 60:
        return "C"
    return "F"

# Call with default bonus
print("Score 88:", calculate_grade(88))

# Call with explicit bonus
print("Score 88 + 5:", calculate_grade(88, 5))

# Named arguments make calls self-documenting
print("Named:", calculate_grade(score=72, bonus=3))

# Functions without return give None
def shout(text):
    print(text.upper())

result = shout("hello")
print("Return value:", result)
Language: python

Lesson Code (Python)

# Defining and calling functions
def calculate_grade(score, bonus=0):
    """Return a letter grade for a score plus optional bonus."""
    total = score + bonus
    if total >= 90:
        return "A"
    if total >= 75:
        return "B"
    if total >= 60:
        return "C"
    return "F"

# Call with default bonus
print("Score 88:", calculate_grade(88))

# Call with explicit bonus
print("Score 88 + 5:", calculate_grade(88, 5))

# Named arguments make calls self-documenting
print("Named:", calculate_grade(score=72, bonus=3))

# Functions without return give None
def shout(text):
    print(text.upper())

result = shout("hello")
print("Return value:", result)

Console Output

Score 88: B
Score 88 + 5: A
Named: C
HELLO
Return value: None

Code Visualization Tips

  • 🧠Trace each function call separately — parameters and locals live in their own box.
  • 🧠Draw the 'return' as the machine dropping the result into the caller's hands.
  • 🧠Use print() inside the function while learning to watch the machine work.

Professional Tips & Tricks

  • ⚡One function = one job. If a function does three things, split it.
  • ⚡Use keyword arguments at call sites: calculate_grade(score=88, bonus=5).
  • ⚡Write the docstring FIRST — it forces you to think about what the function should do.

Python Code Judge & Practice Arena

LeetCode Style

Run real Python 3.12 WebAssembly code directly in your browser against automated test suites.

Solved:0 / 20
0 / 370 XP
Challenges:
Problem 1 of 20

Problem 1: Keyword-Only Bill Calculator 1

Easy+10 XP
Write a function `calculate_bill_1(subtotal, *, tax_rate=0.05, discount=0.0)` that calculates final bill as (subtotal - discount) * (1 + tax_rate) rounded to 2 decimals.
Sample Test Cases:
Input: calculate_bill_1(100, tax_rate=0.1, discount=10)
Expected: 99.0
Input: calculate_bill_1(100)
Expected: 105.0
main.pyPython 3.12 (WASM)
1
2
3
4
5
6
7
8
9
10
11
12
Press Run Code to test or Submit to verify test cases

Test Your Knowledge

Instant feedback

Quick Check: Lesson 12: Python Functions: Parameters, Returns & Type Hints

1 / 20
What is the output of the following function call? def greet(name, greeting='Hello'): return f'{greeting}, {name}!' print(greet('Amol'))

Up next · Continue learning

Lambda, *args & **kwargs

Anonymous one-line functions and flexible functions that accept any number of arguments.

8 mins read40 mins
Start next lesson
Previous: Nested Structures, Aliasing & CopiesNext: Lambda, *args & **kwargs
Made withbyAmol Shukla·amolshukla.online
ASAmol Shukla

AI Developer, Trainer & Agentic AI Expert building practical learning systems and real-world AI applications.

Explore

  • Projects
  • Courses
  • Prompts
  • Skills
  • Contact
  • Experience
  • Blogs

Connect

  • Resume
  • Contact
© 2026 Amol Shukla·Created withbyamolshukla.online
Back to top