Basic Functions
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)}")- 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 resultLet’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!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 nameKeyword 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 BarcelonaParameters 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!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): # CorrectVariable 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: MadridReturning Values with return
The return keyword does two things:
- Returns a value to the code that called the function
- 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)returnis 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.0Functions 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) # Nonereturn 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)) # AdultScope: 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
endLocal 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 definedGlobal 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() # HelloModifying 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() # UnboundLocalErrorYou 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) # 1Global 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 + xDocumenting 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 passOne 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 passKeep 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
Create a function calculate_tip(total, percentage=15) that calculates the tip for a bill. The default percentage is 15%.
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.
Create a function statistics(numbers) that receives a list of numbers and returns a dictionary with: minimum, maximum, sum, average, and count of elements.