Search
Samarth Deyagond

Refactoring in Python

January 30, 2020

01 refactoring 1024

What is refactoring, and why do we need it?

Too often, developers don’t take the time required to refactor and refine their code, resulting in the accumulation of technical debt that needs to be addressed somewhere down the road. Refactoring code is a technique used to improve the non-functional requirements of the code by modifying the external structure without altering the default behavior of the code. By non-functional requirements, it refers to attributes like modularity, readability, testability, maintainability and other enhancements, the absence of which can add technical debt to the code. Refactoring code is like making final touches to a painting.

Refactoring techniques for Python:

While the same refactoring techniques can be employed across different programming languages, this article focuses on refactoring techniques that are more relevant for the Python programming language.

Flat is better than nested

When given a while loop driven by a condition whose iterations check the truth value of another ‘if condition’ inside it, then the loop condition and nested if condition can be combined with an AND logical operator and used as the new loop condition to yield the same result. The code still works perfectly. But when refactored as described, it becomes easier to read and debug.

Below is an example code snippet before refactoring:

def _upheap(self, index):

    parent_index = (index-1)//2
    while index > 0:  # condition driving the while loop
        if self._heap[parent_index] > self._heap[index]:  # if conditional
            self._swap(parent_index, index)
            index = parent_index
            parent_index = (index-1)//2

The refactored code snippet is shown below:

def _upheap(self, index):

    parent_index = (index-1)//2
    while index > 0 and self._heap[parent_index] > self._heap[index]:
        self._swap(parent_index, index)
        index = parent_index
        parent_index = (index-1)//2

Doesn’t the code look much more refined now?

Global variables are always a bad idea

Every function in code has access to global variables. If the global variables are modified, it’s hard to find out which function made the change. Debugging becomes tougher as the code grows. When it comes to readability, strange variables will start popping up from out of nowhere. This is because the global variables in use might be declared somewhere very far into the code. (Don’t you think that declaring the variables close to their usage in the code is a good refactoring idea? This would be especially helpful in Python, which is a dynamically-typed programming language.)

To avoid changing variables that shouldn’t be changed, it is always a good idea not to implement global variables. Global constants, however, are pretty handy.

Consider the below snippet that uses a global variable.

x = 10


def increment_x_by_one():

    global x
    x += 1
    return x


def increment_x_by_two():

    global x
    x += 2
    return x

print(increment_x_by_one())
print(increment_x_by_two())

The output will be as shown below:

11 13

We wanted the second output to be 12, but earlier, the function increment_x_by_one has already been modified with the initial/default value that the variable x is supposed to have. This causes us to have an error.

Now, let’s refactor the code, avoiding the use of a global variable.

def increment_x_by_one(x):
    x += 1
    return x


def increment_x_by_two(x):
    x += 2
    return x

x = 10

print(increment_x_by_one(x))
print(increment_x_by_two(x))

The output now will be accurate, as shown below:

11 12

The result is absolutely as expected, and the function that modified variable x can be easily tracked.

Don’t use magic literals

Many times, string literals and numeric values are used directly, as opposed to being assigned to a variable, even though they imply something significant in the code. It is good to store them as variables with proper names to improve the code’s readability. Those string literals and numeric values might be referred to in multiple places. If they are to be modified, then all their references need to be traced, which is, again, an effort that is mostly prone to error.

Consider the below snippet before refactoring:

if age > 21:
    return True

Now, consider the refactored code:

age_limit = 21
if age > age_limit:
    return True

Comprehending the code block is much easier now, isn’t it?

Remove the commented code

Often working/non-working code sections are commented out when prompted to try an alternative logic, and then they are left “as is”. This makes the readability of the code much more difficult. Also, other comments might get overlooked. So, the commented code should be erased. The best way to keep track of your commented code is via a version control mechanism, like git, using proper commit messages.

Address the redundancy

If two blocks of code are doing the same logic or same set of instructions, then address the redundancy by either extracting a function out of the common code or by placing the redundant instruction on an appropriate line before or after (but only in one place).

Consider the below example before refactoring:

x = 10

if x % 2 == 0:
    print(“The number”, x,is even”)
    print(“The squared of the number is, x**2)
else:
    print(“The number”, x,is odd”)
    print(“The squared of the number is, x**2)

Now, let’s refactor it:

x = 10

if x % 2 == 0:
    print(“The number”, x,is even”)
else:
    print(“The number”, x,is odd”)

print(“The squared of the number is, x**2)

Refactoring not only makes it more readable, but also saves on Python code interpretation time.

Conclusion

Many refactoring tools are available as software packages or extensions for almost every programming language. Some IDE’s (Integrated Development Environments) like PyCharm have built-in refactoring tools. Practice refactoring your code before committing it to the codebase. Besides meeting the non-functional requirements, a neatly refactored code eases debugging, too.

And guess what?! This blog, too, is refactored a couple of times to improve its readability. You see, fundamentals work everywhere!

I hope you found my post on refactoring useful. For more coding tips, keep checking the HPE DEV blog. And if you have any questions, feel free to reach out to me @deyagondsamarth or connect with me on Slack. Happy coding!

Tags

Related

Michael Ferguson

Comparing Standard Library Sorts: The Impact of Parallelism

Jan 30, 2024
Rajeev Kallur

Enabling Python 3 with OpenSSL/FIPS on Microsoft Windows

Dec 21, 2020
Christopher Pasek

HPE OneView Python SDK now available with support for HPE OneView 5.0

Jan 8, 2020
bob.fraser@hpe.com

HPE OneView Python SDK v4.5.0 has been released

Mar 6, 2018
Anusha Y and Sijeesh Kattumunda

Introducing Python SDK for HPE GreenLake Data Services Cloud Console

Mar 29, 2023
BalaSubramanian Vetrivel

Testing OpsRamp APIs with PyTest Fixtures

Jul 26, 2023
Samarth Deyagond

Understanding Concurrency in Python Part 1 - Threading

Feb 10, 2020

HPE Developer Newsletter

Stay in the loop.

Sign up for the HPE Developer Newsletter or visit the Newsletter Archive to see past content.

By clicking on “Subscribe Now”, I agree to HPE sending me personalized email communication about HPE and select HPE-Partner products, services, offers and events. I understand that my email address will be used in accordance with HPE Privacy Statement. You may unsubscribe from receiving HPE and HPE-Partner news and offers at any time by clicking on the Unsubscribe button at the bottom of the newsletter.

For more information on how HPE manages, uses, and protects your personal data please refer to HPE Privacy Statement.