Hello! Welcome to your third lesson in the "Python for Data Science: Foundations" module.
In the last lesson, you learned how to control the flow of your programs using conditional statements and loops. This gave you the power to write code that makes decisions and repeats tasks. As you start writing more complex logic, you'll find that your scripts can become long and repetitive. Today, we'll learn how to manage that complexity.
This lesson is all about making your code more organized, reusable, and easier to read. The learning outcome we'll focus on is to define and use functions with parameters and return values to create modular code. This is a cornerstone of any programming language, and mastering it will dramatically improve your coding skills.
1. Why Do We Need Functions? The DRY Principle
In programming, there's a guiding principle called DRY: Don't Repeat Yourself. If you find yourself writing the same lines of code in multiple places, there's a better way to do it. That's where functions come in.
A function is a named, reusable block of code that performs a specific task. You "define" the block of code once, and then you can "call" it by name whenever you need it, as many times as you want.
Let's watch a short clip that perfectly illustrates this problem and introduces functions as the solution.
Functions in Python are easy 📞
This video from Bro Code, 'Functions in Python are easy', starts with a simple example of singing 'Happy Birthday' multiple times. It clearly shows how repeating code is inefficient and introduces functions as a way to solve this.
Watch the first minute of the video (from 00:00 to 01:00) to understand the motivation behind using functions.
2. Defining and Calling a Basic Function
The syntax for creating a function in Python is straightforward. You use the def keyword, followed by the function's name, a set of parentheses (), and a colon :. The code that belongs to the function is indented on the lines below it.
Let's see the basic syntax in action.
Functions in Python are easy 📞
Continuing with the same video, this next part shows you exactly how to define and then call the 'happy_birthday' function.
Watch from 01:00 to 01:47. Pay close attention to the def keyword, the function name, the parentheses, the colon, and the indented code block.
Here's the structure in a simple example:
# Function Definition
def greet():
print("Hello! Welcome to the lesson on functions.")
print("This line is also inside the function.")
# Function Call
print("This line is outside the function.")
greet() # This executes the code inside the greet() function
greet() # We can call it again!
Output:
This line is outside the function.
Hello! Welcome to the lesson on functions.
This line is also inside the function.
Hello! Welcome to the lesson on functions.
This line is also inside the function.
3. Passing Information to Functions: Parameters and Arguments
Our greet() function is useful, but it does the exact same thing every time. What if we wanted it to greet a specific person? We can make our functions more flexible by passing information into them.
This brings us to two important terms: parameters and arguments.
- Parameter: A variable listed inside the parentheses in the function definition. It's a placeholder.
- Argument: The actual value that is sent to the function when it is called.
Understanding Functions, Parameters, Arguments & Return ...
This article, 'Understanding Functions, Parameters, Arguments...', uses a great analogy of an 'empty box' to explain the difference between parameters and arguments. This distinction is crucial for understanding how functions work.
Read the section 'Parameters vs Arguments'. Focus on the example and the definitions of parameter (placeholder) and argument (actual value).
Let's modify our greet function to use a parameter:
# 'name' is a parameter
def greet_person(name):
print(f"Hello, {name}! Welcome.")
# 'Ankit' and 'Priya' are arguments
greet_person("Ankit")
greet_person("Priya")
Output:
Hello, Ankit! Welcome.
Hello, Priya! Welcome.
The order and number of arguments you pass when calling a function must match the parameters in its definition.
Functions in Python are easy 📞
This video clip shows how to pass multiple arguments to a function and highlights why the order is important.
Watch from 01:47 to 04:30. Notice the error that occurs when the number of arguments doesn't match the parameters, and the mixed-up output when the order is wrong.
Test your understanding!
Write a function called calculate_area that takes two parameters, length and width. Inside the function, calculate the area of a rectangle and print it in a formatted string like "The area is: [area]".
Then, call your function with a length of 10 and a width of 5.
Show answer
def calculate_area(length, width):
area = length * width
print(f"The area is: {area}")
# Calling the function with arguments
calculate_area(10, 5)
# Expected Output: The area is: 50
4. Getting Information Back: The return Statement
So far, our functions have only performed an action (print-ing to the console). But what if we want the function to compute a value that we can use later in our program? For this, we use the return statement.
There are two main types of functions:
- Functions that perform a task (like our
greet_personfunction). - Functions that calculate and return a value.
It's often more powerful to have a function return a value. This makes the function more reusable, as the caller can decide what to do with the result (print it, save it to a file, use it in another calculation, etc.).
Python Functions | Python Tutorial for Absolute Beginners #1
This video from Programming with Mosh provides an excellent explanation of the difference between functions that perform tasks and those that return values. It clearly demonstrates why returning a value is often better.
Watch from 04:43 to 07:30. Pay attention to the argument for why the get_greeting function is more reusable than the greet function.
When a return statement is executed, the function immediately stops and sends the specified value back to where it was called. We can then store this value in a variable.
Let's rewrite our calculate_area function to be a fruitful function—one that returns a value.
def calculate_area_and_return(length, width):
area = length * width
return area # The function ends here and sends 'area' back
# Call the function and store the result
rectangle_area = calculate_area_and_return(10, 5)
# Now we can do whatever we want with the result
print(f"The calculated area is {rectangle_area}.")
if rectangle_area > 40:
print("This is a large rectangle.")
# Output:
# The calculated area is 50.
# This is a large rectangle.
This is a much more modular approach! The function's only job is to calculate; the main part of the script decides what to do with the result.
This image provides a great visual summary of all the parts of a function we've discussed.

A Common Pitfall: print vs. return
A frequent mistake for beginners is to print a value inside a function when they should have used return. If a function doesn't have an explicit return statement, it automatically returns a special value called None.
Returning a value from a function
This page from Runestone Academy discusses 'fruitful functions' and highlights a common beginner mistake: confusing print with return. Understanding this will help you avoid hard-to-find bugs.
Read the section that starts 'Notice something important here...' and pay close attention to the second CodeLens example (clens11_4_2). See how the squareResult variable becomes None because the function only prints the value instead of returning it.
5. Creating Modular Code
The ultimate goal of using functions is to create modular code. This means breaking down a large, complex problem into smaller, manageable, and logical pieces (the functions). Each function has a single, well-defined responsibility.
Imagine writing a program that calculates a user's age. Instead of one long script, we can create separate functions for each task: getting the input, calculating the age, and displaying the result.

This approach makes your code:
- Easier to read and understand: You can see the program's overall structure by looking at the
mainfunction. - Easier to debug: If there's a bug in the age calculation, you know to look inside the
calcAgefunction. - Reusable: You could reuse the
calcAgefunction in another program without any changes.
Conclusion
Congratulations on completing this crucial lesson! You now have the tools to write code that is not just functional, but also clean, organized, and efficient. This skill is absolutely essential for building the larger projects you'll encounter in data science and AI.
Key Takeaways:
- Functions are reusable blocks of code defined with the
defkeyword. They help you follow the DRY (Don't Repeat Yourself) principle. - Parameters are the placeholders in a function's definition, while arguments are the actual values you pass to the function when you call it.
- The
returnstatement allows a function to send a value back to the caller, making it more flexible and reusable than a function that only performs an action likeprint. - Using functions to break down problems into smaller pieces leads to modular code, which is easier to read, test, and maintain.
In our next lesson, we will dive into Python's core data structures: lists, tuples, sets, and dictionaries. You'll see how we can use these structures as arguments to our functions and receive them as return values, unlocking even more powerful ways to handle data.