0%

26- What Is a Function?

A reusable block of code. Give it input, get output. Functions are the building blocks of organized, DRY, and maintainable programs.

You have written code. Lines and lines of it. It runs from top to bottom. It works. But something feels repetitive. You write the same calculation three times. You copy and paste the same validation logic. You repeat yourself. There is a better way. Functions. A function is a named block of code that performs a specific task. You define it once. Then you call it whenever you need that task done. Functions take input (parameters), process it, and optionally return output. They are the first step toward organized, reusable, and maintainable code.

🕯️ Magic Note

The DRY principle: Don’t Repeat Yourself. Functions are the primary tool for following this principle. If you write the same code more than twice, it probably belongs in a function. Functions also make your code more testable and easier to debug.

Why Use Functions?
Functions solve several problems in programming.
  • Reusability: Write once, use many times
  • Organization: Break complex problems into small pieces
  • Readability: Give meaningful names to blocks of code
  • Maintainability: Fix a bug in one place, not everywhere
  • Testing: Test each function independently

Python

# Without a function (repetitive)

print(“Hello, Alice!”)

print(“Hello, Bob!”)

print(“Hello, Charlie!”)

# With a function (clean and reusable)

def greet(name):

print(f”Hello, {name}!”)

greet(“Alice”)

greet(“Bob”)

greet(“Charlie”)

Defining a Function
Use the def keyword, followed by the function name, parentheses, and a colon. The body is indented.

Python

# Basic function definition

def say_hello():

print(“Hello, world!”)

# Calling the function

say_hello() # Output: Hello, world!

💡 Function names should be lowercase with underscores between words (snake_case). Use verbs or verb phrases: calculate_total(), get_user_name(), is_valid().
Function Parameters (Input)
Parameters are variables that receive values when the function is called. They make functions flexible.

Python

# Function with one parameter

def square(x):

print(x ** 2)

square(5) # 25

square(10) # 100

# Function with multiple parameters

def add(a, b):

print(a + b)

add(3, 5) # 8

add(10, 20) # 30

🕯️ Magic Note

When you define a function, the variables in parentheses are called parameters. When you call a function, the values you pass are called arguments. Many programmers use these terms interchangeably, but the distinction is useful.

The return Statement (Output)
Functions can send values back to the caller using return. Without return, a function returns None.

Python

# Function that returns a value

def add(a, b):

return a + b

result = add(3, 5)

print(result) # 8

# Function without return returns None

def say_hello(name):

print(f”Hello, {name}!”)

result = say_hello(“Ali”)

print(result) # None

⚠️ Code after return does not execute. return immediately exits the function.

Python

def check_age(age):

if age < 0:

return “Invalid” # Stops here if age < 0

if age >= 18:

return “Adult”

return “Minor”

print(“This never runs”)

print(check_age(-5)) # “Invalid”

print(check_age(20)) # “Adult”

print(check_age(15)) # “Minor”

Returning Multiple Values
Python functions can return multiple values using tuples. You can unpack them directly.

Python

def get_stats(numbers):

return min(numbers), max(numbers), sum(numbers) / len(numbers)

minimum, maximum, average = get_stats([10, 20, 30, 40, 50])

print(f”Min: {minimum}”) # Min: 10

print(f”Max: {maximum}”) # Max: 50

print(f”Avg: {average}”) # Avg: 30.0

🕯️ Magic Note

Returning multiple values is actually returning a single tuple. Python unpacks it automatically. This is elegant and very Pythonic.

Default Parameters
You can give parameters default values. If the caller does not provide an argument, the default is used.

Python

def greet(name, greeting=”Hello”):

print(f”{greeting}, {name}!”)

greet(“Ali”) # Hello, Ali!

greet(“Sara”, “Hi”) # Hi, Sara!

greet(“Reza”, greeting=”Hey”) # Hey, Reza!

# Multiple default parameters

def create_user(name, age=18, city=”Tehran”):

return {“name”: name, “age”: age, “city”: city}

print(create_user(“Ali”))

print(create_user(“Sara”, 25))

print(create_user(“Reza”, city=”Shiraz”))

⚠️ Default parameters are evaluated once when the function is defined, not each time it is called. Be careful with mutable defaults like lists or dictionaries.

Python

# Dangerous: mutable default

def add_item(item, my_list=[]):

my_list.append(item)

return my_list

print(add_item(1)) # [1]

print(add_item(2)) # [1, 2] (same list!)

# Safe way

def add_item_safe(item, my_list=None):

if my_list is None:

my_list = []

my_list.append(item)

return my_list

Keyword Arguments
You can pass arguments by name. This makes your code more readable and allows you to skip default parameters.

Python

def describe_person(name, age, city, job=”Unknown”):

print(f”{name} is {age}, lives in {city}, works as {job}”)

# Positional arguments (order matters)

describe_person(“Ali”, 25, “Tehran”, “Engineer”)

# Keyword arguments (order does not matter)

describe_person(city=”Shiraz”, name=”Sara”, age=30, job=”Doctor”)

# Mixing positional and keyword (positional first)

describe_person(“Reza”, 28, job=”Teacher”, city=”Isfahan”)

Arbitrary Arguments (*args and **kwargs)
Sometimes you do not know how many arguments will be passed. Use *args for positional arguments and **kwargs for keyword arguments.

Python

# *args collects extra positional arguments as a tuple

def sum_all(*numbers):

return sum(numbers)

print(sum_all(1, 2, 3)) # 6

print(sum_all(10, 20, 30, 40)) # 100

# **kwargs collects extra keyword arguments as a dictionary

def print_info(**info):

for key, value in info.items():

print(f”{key}: {value}”)

print_info(name=”Ali”, age=25, city=”Tehran”)

# Combining both (args first, then kwargs)

def flexible(first, second, *args, **kwargs):

print(f”First: {first}”)

print(f”Second: {second}”)

print(f”Args: {args}”)

print(f”Kwargs: {kwargs}”)

flexible(1, 2, 3, 4, 5, name=”Ali”, age=25)

Docstrings (Documenting Functions)
A docstring is a string immediately after the function definition that describes what the function does. It is accessed with help() or .__doc__.

Python

def calculate_area(length, width):

“””

Calculate the area of a rectangle.

Parameters:

length (float): The length of the rectangle

width (float): The width of the rectangle

Returns:

float: The area of the rectangle

“””

return length * width

# View the docstring

print(calculate_area.__doc__)

# Or use help(calculate_area) in the interpreter

💡 Always write docstrings for non-trivial functions. They help other programmers (and your future self) understand what the function does without reading the implementation.
Scope: Local vs Global Variables
Variables defined inside a function are local. They cannot be accessed outside. Variables defined outside are global and can be read inside functions.

Python

global_var = “I am global”

def my_function():

local_var = “I am local”

print(global_var) # Can read global

print(local_var) # Can read local

my_function()

# print(local_var) # NameError! local_var does not exist outside

⚠️ To modify a global variable inside a function, use the global keyword. But avoid this. It makes code hard to debug. Pass values as parameters and return results instead.
Functions Calling Other Functions
Functions can call other functions. This is how you build complex programs from simple pieces.

Python

def square(x):

return x ** 2

def sum_of_squares(a, b):

return square(a) + square(b)

print(sum_of_squares(3, 4)) # 9 + 16 = 25

Anonymous Functions (lambda)
Lambda functions are small, anonymous functions defined in one line. They are used for short operations.

Python

# Regular function

def double(x):

return x * 2

# Lambda function (same thing)

double_lambda = lambda x: x * 2

print(double(5)) # 10

print(double_lambda(5)) # 10

# Lambda with multiple arguments

add = lambda a, b: a + b

print(add(3, 5)) # 8

# Commonly used with sorted(), filter(), map()

numbers = [1, 2, 3, 4, 5, 6]

evens = list(filter(lambda x: x % 2 == 0, numbers))

print(evens) # [2, 4, 6]

💡 Use lambda for simple, one-time operations. For anything complex or reusable, use a regular def function. Readability matters more than brevity.
Common Mistakes with Functions
  • Forgetting to call the function (writing my_func instead of my_func())
  • Using a mutable default parameter (def func(lst=[]))
  • Returning something but not capturing it (my_func() instead of result = my_func())
  • Trying to modify a global variable without the global keyword
  • Indentation errors (the function body must be indented)
  • Code after return that never executes
Check Your Understanding
  • Write a function called square that returns the square of a number.
  • What is the difference between a parameter and an argument?
  • What does a function return if there is no return statement?
  • How do you write a docstring?
  • What is the problem with def add_item(item, items=[])?
  • When would you use *args?

⚡ Whisper

A function is a spell you write once and cast many times. You give it a name. You tell it what ingredients to expect (parameters). You write the steps inside. Then you decide what it returns. The function waits patiently. It does nothing until you call its name. Then it springs into action. It takes your ingredients, follows the steps, and hands you the result. This is how you build from simple to complex. You write small functions that do one thing well. Then you combine them. A function for greeting. A function for calculating. A function for validating. Each is a trusted servant. Each knows its job. Together they build programs that are clear, organized, and beautiful. Do not be afraid to create many small functions. They are not overhead. They are clarity.

Related posts