Skip to main content
MahiDev — Learn. Build. Become AI-ready.

Lesson 02

Python Function Definition — 4 Approaches with Examples

Master four Python function styles: input from the call or inside the body, print vs return, tuple packing, and the production pattern used in APIs.

Intermediate40 min

What you will learn

  • Function anatomy as a machine: input → process → output
  • Formal parameters vs local variables
  • All 4 input/output approaches
  • Returning multiple values with tuple packing
  • When to use each approach in a real ATM-style example

The big idea

Master the four ways Python functions receive input and produce output. Every function is still the same machine: two parts, three phases.

Function definition, IPO phases, then function call

Remember: def creates the function. The body runs only when called.

You already met this idea on the Functions overview. This lesson is the deep dive: where input comes from and where output goes.


Function anatomy — think of a machine

INPUT a,b into FUNCTION c = a + b return c then OUTPUT 30
Phase Purpose Example
Input Receive values a, b
Process Perform logic c = a + b
Output Send or show the result return c or print(c)
python
def add(a, b):      # INPUT
    c = a + b       # PROCESS
    return c        # OUTPUT

Formal parameters vs local variables

This is a favorite interview question.

def sumop(a, b) labeled Parameters; c = a + b labeled Local
python
def sumop(a, b):      # a, b → formal parameters (function heading)
    c = a + b         # c    → local variable (function body)
    return c
Type Lives where? Purpose
Formal parameter Function heading Receives values from the call
Local variable Function body Stores a temporary result

Both exist only inside the function. Accessing c after sumop returns raises NameError.


The 4 function approaches

The four approaches are combinations of where input comes from and where output goes.

Four colored rows: 1 Call+Return, 2 Inside+Print, 3 Call+Print, 4 Inside+Return

Approach 1 — Input from call → return to call

Most important. This is the style used in almost every production application.

Caller 10,20 into add() which returns 30 to RESULT
python
# ApproachEx1.py — INPUT from call · PROCESS in body · RESULT back to call
def sumop(a, b):          # a, b → formal parameters
    c = a + b
    return c              # c → local variable, sent back

print("type of sumop =", type(sumop))   # <class 'function'>

k, v = 10.0, 20.0
res = sumop(k, v)
print("Sum({}, {}) = {}".format(k, v, res))

Output:

code
type of sumop = <class 'function'>
Sum(10.0, 20.0) = 30.0

Memory trick: Call gives → return comes back.

Use this for utilities, APIs, ML models, and reusable business logic. Same input always gives the same output — easy to test: assert sumop(2, 3) == 5.


Approach 2 — Input inside → print inside

A self-contained function. The caller just writes sumop() with no arguments.

Green box: a = input, b = input, c = a+b, print(c) — no return value
python
# ApproachEx2.py — input and output both inside the body
def sumop():
    a = 5.0               # terminal version: float(input("Enter First Value:"))
    b = 3.0
    c = a + b
    print("Sum({}, {}) = {}".format(a, b, c))

sumop()

Output:

code
Sum(5.0, 3.0) = 8.0

Best for quick scripts and console programs. Harder to test because the function prints instead of returning a value.


Approach 3 — Input from call → print inside

Useful when the function is responsible for displaying information. Data comes from outside; formatting stays inside.

Caller 15,5 into function that prints Sum = 20
python
# ApproachEx3.py — params from call · print inside · no return
def sumop(k, v):
    r = k + v
    print("Sum({}, {}) = {}".format(k, v, r))

k, v = 15.0, 5.0
sumop(k, v)

Output:

code
Sum(15.0, 5.0) = 20.0

There is no return. The function only displays output. print(sumop(15, 5)) would also print None.


Approach 4 — Input inside → return to call

Internal input with reusable output. The caller decides what to do with the result.

Function reads a,b then returns a,b,c to caller unpack k,v,r
python
# ApproachEx4.py — input inside · return multiple values (tuple)
def sumop():
    a, b = 4.0, 6.0
    c = a + b
    return a, b, c        # packed into a tuple automatically

k, v, r = sumop()         # unpack
print("Sum({}, {}) = {}".format(k, v, r))

res = sumop()             # res is <class 'tuple'>
print("Sum({}, {}) = {}".format(res[0], res[1], res[2]))
print("Sum({}, {}) = {}".format(res[-3], res[-2], res[-1]))

Output:

code
Sum(4.0, 6.0) = 10.0
Sum(4.0, 6.0) = 10.0
Sum(4.0, 6.0) = 10.0

Multiple return values — tuple packing

A hidden feature of Python: comma-separated return packs a tuple.

return a, b, c becomes tuple (4, 6, 10) then unpack x, y, total
python
def values():
    return 4, 6, 10

a, b, c = values()
print(a)   # 4
print(c)   # 10
print(type(values()))   # <class 'tuple'>

Side-by-side comparison

Approach Input Output Best use
1 Function call return Production code, APIs, tests
2 Inside body print Console scripts
3 Function call print Reports / display helpers
4 Inside body return (often a tuple) Data collection

Learn Approach 1 first — it is the standard in professional Python.

python
def add1(a, b):
    return a + b

def add2():
    x, y = 10, 20
    print("Approach 2:", x + y)

def add3(a, b):
    print("Approach 3:", a + b)

def add4():
    x, y = 10, 20
    return x + y

print("Approach 1:", add1(10, 20))
add2()
add3(10, 20)
print("Approach 4:", add4())

Real-world example — ATM balance

Approach 1 is ideal: the function does not know where the money comes from. It only performs logic.

python
def withdraw(balance, amount):
    if amount <= balance:
        return balance - amount
    return balance

new_balance = withdraw(5000, 1200)
print(new_balance)   # 3800

Why this is good design:

  • Input comes from the caller
  • The function has one responsibility
  • The result is returned for further use (save, log, API response)

This is the same pattern used in FastAPI services, ML preprocessing, and business logic.


Common mistakes

1. Calling before defining → NameError

python
# add(2, 3)   # NameError — always define first

def add(a, b):
    return a + b

print(add(2, 3))   # 5

2. Wrong number of arguments → TypeError

python
def add(a, b):
    return a + b

# add(5)   # TypeError: add() missing 1 required positional argument: 'b'
print(add(5, 3))

3. Using a local variable outside → NameError

python
def calc():
    total = 10
    return total

# print(total)   # NameError — total lives inside calc() only
print(calc())    # 10

4. Forgetting return → None

python
def add(a, b):
    a + b          # computed, then thrown away

print(add(2, 3))   # None

def add_ok(a, b):
    return a + b

print(add_ok(2, 3))   # 5

print() shows a value on screen. It does not replace return. The caller cannot reuse a printed number.


Practice in the playground

The playground above already runs all four approaches plus the ATM example. Try:

  1. Change sumop(10, 20) to sumop(100, 25) — confirm Approach 1 prints 125.
  2. Add print(show_sum(15, 5)) and notice the extra None.
  3. Change the ATM call to withdraw(5000, 9000) — balance should stay 5000.

Next: Parameters and Arguments — five ways to pass data into these functions.

Loading playground…

Check your understanding

Function approaches quiz

Which approach is used most often in production applications?

1 / 10