You will often use functions or procedures that have been written by someone else (perhaps as part of a library). You don't need to understand the internal details of those procedures; you might not even have access to the source code! However, documentation for the Application Programming Interface (API) will tell you everything you need to know to use the functions successfully. Using procedures without worrying about internal details is called procedural abstraction.

You might also find yourself writing the same code or block of logic more than one time within your program. There are many advantages to creating functions out of those similar blocks of code. By creating functions that can be reused in different situations, you are demonstrating modularity.

Using Procedural Abstraction
Programmers love to reuse code. By simply calling a function from your program, you can accomplish a task without duplicating the statements in the body of the function in your own program. Consider some of the built-in Python functions you have already used like print() and input(). You can call these functions - as demonstrated by the program below - without any idea how they really work. This is procedural abstraction in action!

Try It Now


To call a function, you just need to know the function's name, parameters, expected behavior, and expected output. This kind of information can be found in API documentation; you don't need to look at the function's source code. print() and input() may use very complex logic to display information to the screen or get text input from the user, but they are simple for you to use.

Similarly, we can import modules like random to use other procedures without knowing how they are coded. Look at this example:

Try It Now


To call the randrange() function in the random library, we just need to write the function name, provide a couple of numeric parameters, and understand that a random number between those parameters will be returned as a result.

Generating a random (or random-like) number is actually pretty complex for a computer. Procedures like randrange() will use an algorithm to produce a result. There are different algorithms to produce random numbers, and some may work faster or produce better results than others. What happens if a new version of Python includes a different internal algorithm inside the randrange() function? Your code will continue to work just fine as long as the function name, parameters, behavior, and results are the same!

Functions can be improved internally without impacting anyone who is using that function.
Advantages of Procedural Abstraction
Let's summarize the main advantages of using procedural abstraction.

Your programs are easier to write. You don't need to understand the internal workings of existing functions.
Your program code is shorter. You don't have to duplicate code that already exists.
Your program code is less complex and simpler to read and understand. It is easier to understand the purpose of a single statement making a function call as compared to reading a block of code to figure out what it is doing.
Improvements can be made to a function without impacting users. As long as your function name, parameters, behavior, and return data are the same, you can make other internal improvements to functions and programs using it will continue to work.
When writing your own programs, you should look for opportunities to use procedural abstraction. For example, if you identify a task that would fit nicely into a function, try looking for a function in a Python library that already does that task before writing your own logic. If you try to reinvent everything from scratch, your programs will be more complex, harder to write, and harder to understand.

Modularity
Careful use of functions or procedures allows programmers to break larger programs into smaller pieces. This is called modularity. Instead of writing a single, complex, hard-to-understand, and hard-to-test block of logic, we write a main program that uses modules or functions to do much of the work. Our main program code is then easier to understand, write, and maintain.

To use modularity, you need to look for opportunities to create functions that can be used more than once in your program. Let's explore an example program that can be simplified with modularity. We want our program to determine letter grades (A, B, C, D, F) based on a numeric score between 0 and 100%. A score of 90% to 100% is an A, 80% to 89% is a B, and so on.

Grade Scale



Our program needs to convert three numeric grades. If we do not use modularity to create a procedure, our code might look something like this.

grade1 = 90

if grade1 >= 90:
print("A")
elif grade1 >= 80:
print("B")
elif grade1 >=70:
print("C")
elif grade1 >= 60:
print("D")
else:
print("F")

grade2 = 77
if grade2 >= 90:
print("A")
elif grade2 >= 80:
print("B")
elif grade2 >=70:
print("C")
elif grade2 >= 60:
print("D")
else:
print("F")

grade3 = 85
if grade3 >= 90:
print("A")
elif grade3 >= 80:
print("B")
elif grade3 >=70:
print("C")
elif grade3 >= 60:
print("D")
else:
print("F")
Copy
As you can see, this code is long and has duplicate logic. What happens if we wanted to fix an error in an "if" statement or change the grading scale? You would need to find and fix those problems in every one of those similar sets of "if" blocks. As a wise programmer, you identify these "if" statements as something that can be wrapped up as a neat function that can be called more than once.

Designing Functions
When you want to define a function, be sure to identify these important characteristics:

Function name
Function parameters
Function return data
Function behavior
In this example, we will call our function "calculateGrade". It will have one input parameter, which is the numeric score from 0 - 100. It will not have any return data. Internally, the function will compare the input parameter against the grading scale to determine the letter grade ("A", "B", "C", "D", "F") and print that grade to the screen.

With only this function design - but no written function yet - you can already envision your main program getting reduced to just 3 statements!

calculateGrade(92)
calculateGrade(60)
calculateGrade(85)
Copy
Let's fully implement this program, including the function, and observe the modularity at work.

Try It Now




While not all procedures have parameters, those that do tend to be more flexible and powerful. Without any input parameters, a function will always do the same thing each time it is called. When you provide parameters, the function logic can make decisions and produce different outputs or behavior based on that input. A more flexible function is more useful and can often handle a wider variety of tasks.
Solving Problems with Modularity
Given a large problem or task, programmers will usually start thinking about modularity right away. It is often easier to split a large program into a series of smaller problems and then solve those smaller problems individually. When we have a collection of modules that work individually, we can combine them to solve the larger problem.



Let's design a program that will display the words to the "Old MacDonald Had a Farm" nursery rhyme. The lyrics are repetitive but change slightly each time you move to a new animal that makes a new sound. This example shows the lyrics for a "cow" that makes a "moo" sound.

"Old MacDonald had a farm, E-I-E-I-O. And on this farm he had a cow, E-I-E-I-O. With a moo moo here and a moo moo there, here a moo, there a moo, everywhere a moo moo, Old MacDonald had a farm, E-I-E-I-O."
The words in bold (cow and moo) change each time a new animal is added, but the rest of the words are the same. We can generalize the output for any <animal> making any <sound> as shown below.

"Old MacDonald had a farm, E-I-E-I-O. And on this farm he had a <animal>, E-I-E-I-O. With a <sound> <sound> here and a <sound> <sound> there, here a <sound>, there a <sound>, everywhere a <sound> <sound>, Old MacDonald had a farm, E-I-E-I-O."
Our program wants to repeat these lyrics over and over again with small changes. This seems like a great place for a function! Clearly, our function should have two parameters - an animal and a sound. We could also decide that our function will internally print the final text on the screen instead of returning any data.

def oldMacDonald(animal, sound):
Copy
With this function defined, we can imagine our main program works by simply calling the function with different parameters.

oldMacDonald("cow", "moo")
oldMacDonald("pig", "oink")
Copy
Now we just need to write the function itself, and our program is done. Check out the results below!

Try It Now




Is there anything we could do to make this program even more modular? Let's review our oldMacdonald() function and see if any parts of that logic seem repetitive. While much of the output text is unique, the "E-I-E-I-O. " text is repeated several times. We could create a small function to help build the overall result. It is common for functions to call other functions to get things done!

Let's define a small function called EI() that will take no parameters and simply return the "E-I-E-I-O." text. We'll then call that function as needed from within oldMacDonald().

Try It Now


The program output is the same, but the program logic is more modular. In fact, you can easily create a new version of the song by changing the EI() function to return something else! Can you produce a song that uses "W-A-W-A-O" instead?

Let's review our program in terms of modularity and procedural abstraction.

To create a modular program, we studied the desired output or behavior and found patterns that repeated. We then wrote a function to handle that repetitive task. To make the function more powerful, we added input parameters so the function could produce the right output for any kind of animal. We refined the program by continuing to divide one task into smaller pieces that worked together.

Our main program logic was reduced to three statements by using procedural abstraction. It simply calls a function several times with different parameters to get things done. When we first visualized the main logic, we didn't even know how the oldMacdonald() function would work internally, but we knew what input data it needed and what it would do for the main program. We also demonstrated that the smaller modules or functions could be changed slightly without breaking the main program logic.

What is procedural abstraction? How do you practice it when writing your own programs?
What are some examples of functions you have called without understanding their internal workings?
What are the advantages of using procedural abstraction?
What is modularity? How do you practice it when writing programs?
What key pieces of information do you need when designing your own functions?
How do you make procedures more flexible and powerful?
How do modular programs reduce code complexity, improve readability, and support maintainability?

Procedural abstraction is the practice of using functions or procedures without needing to understand their internal details. When writing your own programs, you can practice procedural abstraction by calling functions or procedures with the knowledge of their expected behavior, input/output parameters, and function name without looking at the source code.

Examples of functions you may have called without understanding their internal workings include print(), input(), and randrange() in Python, among others.

The advantages of using procedural abstraction include easier program writing, shorter code, simpler code readability, and the ability for improvements to be made to functions without impacting users as long as the function signature remains the same.

Modularity is the practice of breaking down large programs into smaller, reusable pieces like functions or procedures. To practice modularity when writing programs, you can identify repetitive tasks or logic and encapsulate them into separate functions that can be reused.

When designing functions, you need to consider the function name, input parameters, return data, and behavior. Input parameters allow functions to be more flexible and powerful as they can produce different outputs based on the provided input.

Modular programs reduce code complexity by breaking down tasks into smaller pieces, improve readability by organizing code into logical components, and support maintainability by allowing changes to be made to individual functions without affecting the entire program.

1. What is procedural abstraction? How do you practice it when writing your own programs?

2. What are some examples of functions you have called without understanding their internal workings?
3. What are the advantages of using procedural abstraction?
4. What is modularity? How do you practice it when writing programs?
5. What key pieces of information do you need when designing your own functions?
6. How do you make procedures more flexible and powerful?
7. How do modular programs reduce code complexity, improve readability, and support maintainability?

1. Procedural abstraction is the practice of using functions or procedures without needing to understand their internal implementation details. When writing your own programs, you practice procedural abstraction by focusing on using functions based on their expected behavior, input parameters, and output, without needing to know how they are implemented internally.

2. Some examples of functions that are commonly used without understanding their internal workings include built-in functions like print(), input() in Python, library functions like randrange() from the random module, and functions from third-party libraries that you may use in your programs.

3. The advantages of using procedural abstraction include easier program writing, code reusability, shorter and more concise code, improved code readability, easier maintenance, and the ability to make changes to the internal implementation of functions without affecting the overall functionality as long as the function signature remains the same.

4. Modularity is the practice of breaking down a large program into smaller, more manageable pieces or modules. When writing programs, you practice modularity by identifying common tasks or logic that can be encapsulated into functions, classes, or modules, making your code more organized, easier to maintain, and promoting code reusability.

5. When designing your own functions, you need to consider key pieces of information such as the function name (to clearly identify the purpose of the function), input parameters (to pass data into the function for processing), return type (what the function will output), and the behavior (what the function will do with the input parameters to produce the output).

6. To make procedures more flexible and powerful, you can use parameters to allow the function to handle different inputs and produce different outputs based on the provided arguments. You can also design functions to be reusable and modular so that they can be called in different parts of the program.

7. Modular programs reduce code complexity by breaking down tasks into smaller, more manageable modules, improve readability by organizing code into logical components, enhance maintainability by allowing for easier debugging and updates to individual modules without affecting the entire program, and support reusability by enabling the reuse of functions or modules in different parts of the program or in future projects.

Make the sentences shorter

1. Procedural abstraction is using functions without understanding their internal details. Practice it by focusing on behavior, inputs, and outputs in your programs.

2. Examples of functions used without knowing internal workings are print() and input() in Python, randrange() from the random module, and third-party library functions.

3. Advantages of using procedural abstraction include easier writing, code reusability, concise code, improved readability, easier maintenance, and flexible internal changes.

4. Modularity is breaking a program into smaller modules. Practice it by identifying common tasks to encapsulate into functions or classes, making code organized and reusable.

5. When designing functions, consider the function name, input parameters, return type, and behavior for clarity and functionality.

6. Make functions more flexible and powerful by using parameters for different inputs and outputs and designing for reusability and modularity.

7. Modular programs reduce complexity, improve readability, and support maintainability by breaking tasks into smaller modules, organizing code logically, and allowing for easier updates and reusability.