Basic Functions

Module Objectives

By completing this module you will be able to:

  • Understand what functions are and why to use them
  • Create functions with def
  • Use parameters and arguments
  • Return values with return
  • Understand variable scope

Why Do You Need Functions?

Imagine you’re a chef in a restaurant. Every time a customer orders an omelet, you could explain from scratch how to beat eggs, which pan to use, how much oil to add… Or you could have a recipe that you always follow. The recipe is written once and used a thousand times.

Functions are the recipes of programming. Instead of repeating the same code over and over, you write it once, give it a name, and use it when you need it.

The Problems of Not Using Functions

Look at this code without functions:

 1# Calculate the area of a 5x3 rectangle
 2area1 = 5 * 3
 3print(f"Area: {area1}")
 4
 5# Calculate the area of another 10x7 rectangle
 6area2 = 10 * 7
 7print(f"Area: {area2}")
 8
 9# And another 8x4
10area3 = 8 * 4
11print(f"Area: {area3}")

What if you now want to add the perimeter too? You’d have to modify three places. And if you have 50 rectangles? It becomes a nightmare.

The DRY Principle: Don’t Repeat Yourself

In programming there’s a golden rule: don’t repeat yourself. If you’re copying and pasting code, something is wrong. Functions are the solution:

1def calculate_area(base, height):
2    return base * height
3
4# Now it's easy and clean
5print(f"Area: {calculate_area(5, 3)}")
6print(f"Area: {calculate_area(10, 7)}")
7print(f"Area: {calculate_area(8, 4)}")
Benefits of Functions
  • Reuse: Write once, use many times
  • Organization: Divide big problems into small pieces
  • Readability: calculate_price() is clearer than 20 lines of code
  • Maintenance: If there’s a bug, you fix it in one place

Your First Function

A function is like a magic box: you put something in (input), it does its work (processing), and gives you something back (output).

graph LR
    A["Input<br/>(parameters)"] --> B["Function<br/>processing"]
    B --> C["Output<br/>(return)"]

Basic Syntax

1def function_name(parameter1, parameter2):
2    # Function body (what it does)
3    result = parameter1 + parameter2
4    return result

Let’s break down each part:

Element What it is Example
def Keyword indicating “I’m going to define a function” def
Name Function identifier (lowercase, with underscores) calculate_price
Parentheses Contain the input parameters (price, discount)
Colon Indicates the function body starts :
Body Indented code that runs when the function is called return price * 0.9
return Returns a value (optional) return result

Step by Step Example

 1# 1. DEFINE the function (write the recipe)
 2def greet(name):
 3    message = f"Hello, {name}!"
 4    return message
 5
 6# 2. CALL the function (use the recipe)
 7result = greet("Ana")
 8print(result)  # Hello, Ana!
 9
10# You can call it as many times as you want
11print(greet("Luis"))   # Hello, Luis!
12print(greet("Maria"))  # Hello, Maria!
Define vs Call

Defining a function (with def) is like writing a recipe. It doesn’t cook anything, it just saves the instructions.

Calling a function (writing its name with parentheses) is cooking following the recipe. That’s when the code actually runs.


Parameters and Arguments

Imagine a registration form. The form has empty fields (name, email, phone). When someone fills it out, those fields have concrete values.

  • Parameters are the empty fields on the form (the definition)
  • Arguments are the concrete values you fill in (the call)
1#          parameters ↓
2def register(name, email):
3    print(f"User: {name}, Email: {email}")
4
5#            arguments ↓
6register("Ana Garcia", "[email protected]")

Positional Parameters

Order matters. The first argument goes to the first parameter, the second to the second…

1def introduce(name, age, city):
2    print(f"I'm {name}, I'm {age} years old and I live in {city}")
3
4introduce("Ana", 25, "Madrid")      # Correct
5introduce(25, "Ana", "Madrid")      # Error! 25 is not a name

Keyword Arguments

You can specify the parameter name when calling. This way order doesn’t matter:

1introduce(city="Barcelona", name="Luis", age=30)
2# I'm Luis, I'm 30 years old and I live in Barcelona

Parameters with Default Values

Sometimes you want a parameter to have a “factory” value if not specified:

1def greet(name, greeting="Hello"):
2    return f"{greeting}, {name}!"
3
4print(greet("Ana"))              # Hello, Ana!
5print(greet("Ana", "Good morning"))  # Good morning, Ana!
Important Rule

Parameters with default values must come after those without. Otherwise, Python gets confused:

1# Bad
2def function(a=1, b):  # SyntaxError!
3
4# Good
5def function(b, a=1):  # Correct

Variable Number of Arguments

Sometimes you don’t know how many arguments you’ll receive. Python has two tools for this:

 1# *args: receives multiple positional arguments as tuple
 2def add_all(*numbers):
 3    return sum(numbers)
 4
 5print(add_all(1, 2))           # 3
 6print(add_all(1, 2, 3, 4, 5))  # 15
 7
 8# **kwargs: receives keyword arguments as dictionary
 9def create_profile(**data):
10    for key, value in data.items():
11        print(f"{key}: {value}")
12
13create_profile(name="Ana", age=25, city="Madrid")
14# name: Ana
15# age: 25
16# city: Madrid

Returning Values with return

The return keyword does two things:

  1. Returns a value to the code that called the function
  2. Terminates the function execution immediately

The Difference Between print() and return

This is a very common confusion among beginners. Think of a worker:

  • print() is like a worker who does their job and tells you what they did (shows on screen)
  • return is like a worker who does their job and hands you the result (you can use it later)
 1# Only shows on screen, doesn't return anything useful
 2def add_print(a, b):
 3    print(a + b)
 4
 5# Returns the value, you can use it later
 6def add_return(a, b):
 7    return a + b
 8
 9# With print: you can't do anything with the result
10result1 = add_print(2, 3)  # Shows: 5
11print(result1)             # None (it didn't return anything)
12
13# With return: the result is useful
14result2 = add_return(2, 3)
15print(result2)             # 5
16print(result2 * 2)         # 10 (you can operate on it)

Returning Multiple Values

Python allows returning several values at once (actually, it returns a tuple):

 1def analyze_numbers(list):
 2    minimum = min(list)
 3    maximum = max(list)
 4    average = sum(list) / len(list)
 5    return minimum, maximum, average
 6
 7# Unpack the values
 8smallest, largest, mean = analyze_numbers([1, 5, 3, 9, 2])
 9print(f"Minimum: {smallest}, Maximum: {largest}, Average: {mean}")
10# Minimum: 1, Maximum: 9, Average: 4.0

Functions Without return

If a function has no return, it automatically returns None:

1def greet(name):
2    print(f"Hello, {name}")
3    # No return
4
5result = greet("Ana")  # Shows: Hello, Ana
6print(result)          # None

return Terminates the Function

When Python encounters a return, the function ends immediately:

 1def check_age(age):
 2    if age < 0:
 3        return "Invalid age"  # If negative, ends here
 4    if age < 18:
 5        return "Minor"        # If minor, ends here
 6    return "Adult"            # If we get here, it's an adult
 7
 8print(check_age(-5))   # Invalid age
 9print(check_age(15))   # Minor
10print(check_age(25))   # Adult

Scope: Where Do Variables Live?

Imagine a house with rooms. What happens in the kitchen, stays in the kitchen. If you leave a pan in the kitchen, it doesn’t magically appear in the bedroom.

In programming, variables also have their “room”: it’s what we call scope.

graph TB
    subgraph Global["Global Scope (the whole house)"]
        A["global_variable = 'visible throughout the house'"]
        subgraph Function["Local Scope (one room)"]
            B["local_variable = 'only visible in here'"]
        end
    end

Local Variables

Variables created inside a function are local. They only exist while the function is running:

1def my_function():
2    message = "I'm local"  # Local variable
3    print(message)
4
5my_function()      # I'm local
6print(message)     # NameError: 'message' is not defined

Global Variables

Variables created outside of functions are global. They’re visible from anywhere:

1greeting = "Hello"  # Global variable
2
3def greet():
4    print(greeting)  # Can READ the global variable
5
6greet()  # Hello

Modifying Global Variables (and Why to Avoid It)

If you try to modify a global variable inside a function, Python creates a new local variable:

1counter = 0
2
3def increment():
4    counter = counter + 1  # Error! Python thinks it's local
5    print(counter)
6
7increment()  # UnboundLocalError

You can use global to force access, but it’s not recommended:

1counter = 0
2
3def increment():
4    global counter  # "Use the global variable, don't create a local one"
5    counter = counter + 1
6
7increment()
8print(counter)  # 1
Avoid Global Variables

Global variables make code hard to understand and debug. If a function needs a value, pass it as a parameter. If it needs to return something, use return.

1# Bad: uses global variable
2total = 0
3def add(x):
4    global total
5    total += x
6
7# Good: uses parameters and return
8def add(total, x):
9    return total + x

Documenting Functions (docstrings)

A docstring is text that explains what a function does. It goes right after the def line, in triple quotes:

 1def calculate_bmi(weight, height):
 2    """
 3    Calculates the Body Mass Index (BMI).
 4
 5    Args:
 6        weight: Weight in kilograms
 7        height: Height in meters
 8
 9    Returns:
10        The BMI as a decimal number
11    """
12    return weight / (height ** 2)

Why Document?

  • For others: Whoever reads your code (including your future self) will understand what each function does
  • For tools: IDEs and editors show the docstring when you hover over a function
  • For documentation: Tools like Sphinx generate automatic documentation
1# View the docstring of a function
2print(calculate_bmi.__doc__)
3
4# Or using help()
5help(calculate_bmi)

Best Practices

Name Functions with Verbs

The name should describe what the function does:

 1# Bad: nouns or confusing names
 2def user_data():
 3    pass
 4
 5def process():
 6    pass
 7
 8# Good: verbs that indicate action
 9def get_user_data():
10    pass
11
12def process_order():
13    pass
14
15def calculate_total():
16    pass
17
18def validate_email():
19    pass

One Function = One Task

Each function should do one thing well. If it does many things, split it:

 1# Bad: does too many things
 2def process_user(data):
 3    # Validates data
 4    # Saves to database
 5    # Sends welcome email
 6    # Generates statistics
 7    pass
 8
 9# Good: small and focused functions
10def validate_data(data):
11    pass
12
13def save_user(data):
14    pass
15
16def send_welcome_email(email):
17    pass

Keep Functions Short

If a function has more than 20-30 lines, it probably does too much. Consider splitting it.

When to Create a Function

Create a function when:

  • You repeat code more than twice
  • A block of code does a specific task you can name
  • You want to simplify complex code by dividing it into parts
  • You need to test a part of the code in isolation

Summary

Concept Description Example
def Defines a function def my_function():
Parameters Input variables (in definition) def sum(a, b):
Arguments Concrete values (in call) sum(2, 3)
return Returns a value and ends function return result
Default value Parameter with “factory” value def f(x=10):
*args Multiple positional arguments def f(*args):
**kwargs Multiple keyword arguments def f(**kwargs):
Local scope Variables inside function Only visible in function
Global scope Variables outside functions Visible throughout program
Docstring Function documentation “““Text…”””

Practical Exercises

Exercise 1: Tip Calculator

Create a function calculate_tip(total, percentage=15) that calculates the tip for a bill. The default percentage is 15%.

 1def calculate_tip(total, percentage=15):
 2    """
 3    Calculates the tip for a bill.
 4
 5    Args:
 6        total: The total of the bill
 7        percentage: The tip percentage (default 15%)
 8
 9    Returns:
10        The amount of tip to leave
11    """
12    tip = total * (percentage / 100)
13    return tip
14
15# Tests
16print(calculate_tip(100))      # 15.0 (15% default)
17print(calculate_tip(100, 20))  # 20.0 (20%)
18print(calculate_tip(50, 10))   # 5.0 (10%)
Exercise 2: Password Validator

Create a function validate_password(password) that verifies if a password meets:

  • At least 8 characters
  • At least one uppercase letter
  • At least one number

Returns True if it meets all criteria, False if not.

 1def validate_password(password):
 2    """
 3    Validates if a password meets security requirements.
 4
 5    Args:
 6        password: The password to validate
 7
 8    Returns:
 9        True if valid, False if not
10    """
11    # At least 8 characters
12    if len(password) < 8:
13        return False
14
15    # At least one uppercase
16    has_uppercase = any(c.isupper() for c in password)
17    if not has_uppercase:
18        return False
19
20    # At least one number
21    has_number = any(c.isdigit() for c in password)
22    if not has_number:
23        return False
24
25    return True
26
27# Tests
28print(validate_password("abc"))           # False (too short)
29print(validate_password("abcdefgh"))      # False (no uppercase or number)
30print(validate_password("Abcdefgh"))      # False (no number)
31print(validate_password("Abcdefg1"))      # True
Exercise 3: List Statistics

Create a function statistics(numbers) that receives a list of numbers and returns a dictionary with: minimum, maximum, sum, average, and count of elements.

 1def statistics(numbers):
 2    """
 3    Calculates basic statistics for a list of numbers.
 4
 5    Args:
 6        numbers: List of numbers
 7
 8    Returns:
 9        Dictionary with minimum, maximum, sum, average and count
10    """
11    if not numbers:
12        return None
13
14    return {
15        "minimum": min(numbers),
16        "maximum": max(numbers),
17        "sum": sum(numbers),
18        "average": sum(numbers) / len(numbers),
19        "count": len(numbers)
20    }
21
22# Test
23data = [10, 20, 30, 40, 50]
24result = statistics(data)
25print(result)
26# {'minimum': 10, 'maximum': 50, 'sum': 150, 'average': 30.0, 'count': 5}

Quiz

🎮 Quiz: Basic Functions

0 / 0
Loading questions...

Previous: Files Next: Advanced Functions