Blog Post #142: Part 5 Recap & Project: Refactoring to Pythonic

Welcome to the final post of Part 5! In this section, we moved beyond just writing code that works and began exploring the philosophy of writing code that is clean, efficient, and “Pythonic.”

To wrap up, we’ll first briefly recap the key Pythonic idioms and features we’ve learned. Then, for our project, we will do something new: we’ll take a piece of poorly written code and refactor it, step-by-step, into an elegant, Pythonic solution.

Part 5 Recap: The Pythonic Way

This part was all about style, elegance, and using Python’s best features. We started with the philosophy of Pythonic code and the Zen of Python (Post #113). We then saw this in action with:

Project: Refactoring a C-Style Script

Refactoring is the process of restructuring existing computer code—changing its internal structure—without changing its external behavior. It’s about improving the code’s design, readability, and maintainability.

Our Task: We have a script that takes a list of numbers. It needs to produce a new list of strings, where each string is formatted as “The square of X is Y”, but only for the positive, even numbers in the original list.

The “Before” Script

Here is a script that correctly solves the problem, but in a verbose, non-Pythonic, “C-style” way. It uses a manual while loop, unnecessary nested ifs, and clumsy string concatenation.

numbers = [1, 2, 3, -4, 5, 6, -7, 8, 9, 10]

def process_list_unpythonic(data):
    results = []
    i = 0
    while i < len(data):
        num = data[i]
        # Check conditions separately
        if num > 0:
            if num % 2 == 0:
                # Manual string concatenation
                square = num ** 2
                line = "The square of " + str(num) + " is " + str(square)
                results.append(line)
        i = i + 1 # Manual increment
    return results

processed_data = process_list_unpythonic(numbers)
print(processed_data)

Step 1: Refactor the Loop and Conditions

The first and most obvious improvement is to replace the manual while loop with a direct for loop (as we learned in Post #114) and combine the nested if statements with and.

def process_list_better(data):
    results = []
    for num in data:
        # Combine the conditions
        if num > 0 and num % 2 == 0:
            square = num ** 2
            line = "The square of " + str(num) + " is " + str(square)
            results.append(line)
    return results

This is already much better, safer, and easier to read.

Step 2: Refactor the String Formatting

Next, let’s replace the messy string concatenation with a clean and readable f-string (Post #115) and perform the calculation directly inside it.

def process_list_even_better(data):
    results = []
    for num in data:
        if num > 0 and num % 2 == 0:
            # Use a modern f-string
            line = f"The square of {num} is {num**2}"
            results.append(line)
    return results

This is a huge improvement in readability.

Step 3: The Final Refactoring – A List Comprehension

This is the ultimate Pythonic step. Our entire for loop is a classic “transform and filter” pattern. As we learned in Post #121, this is a perfect candidate for a list comprehension.

def process_list_pythonic(data):
    """Processes a list of numbers in a Pythonic way."""
    return [f"The square of {num} is {num**2}" for num in data if num > 0 and num % 2 == 0]

# Let's run the final version
processed_data = process_list_pythonic(numbers)
print(processed_data)

The output is the same as our original script, but the implementation is vastly superior:

[‘The square of 2 is 4’, ‘The square of 6 is 36’, ‘The square of 8 is 64’, ‘The square of 10 is 100’]

This single line of code is more readable, more efficient, and perfectly expresses the logic of our task.

Congratulations and What’s Next for Part 6

Fantastic! You’ve not only learned what “Pythonic” code is, but you’ve also practiced the valuable skill of refactoring. This ability to see a verbose block of code and transform it into a clean, idiomatic solution is a key skill of an experienced Python developer.

So far, all of our programs have “forgotten” everything the moment they finish running. The data we create lives only in memory. To build truly persistent applications, we need to be able to save data to and read data from files.

In Part 6: File Handling and Error Management, we will learn how to make our programs interact with the file system, and how to gracefully handle the errors that can occur when things go wrong.

Author

Debjeet Bhowmik

Experienced Cloud & DevOps Engineer with hands-on experience in AWS, GCP, Terraform, Ansible, ELK, Docker, Git, GitLab, Python, PowerShell, Shell, and theoretical knowledge on Azure, Kubernetes & Jenkins. In my free time, I write blogs on ckdbtech.com

Leave a Comment