Quick Python Refactoring Tips

Quick Python Refactoring Tips

8 quick Python refactoring tips for cleaner and more pythonic code.

Featured on Hashnode

In this Tutorial I show you 8 quick Python refactoring tips for cleaner and more pythonic code. This is part 1 of my refactoring code series.

Part 2 can be found here: Refactoring Part 2

1: Merge nested if statements

Let’s start pretty simple. Instead of having nested if statements like this, just merge them into one.

if a:
    if b:
        pass

# -> refactor
if a and b:
    pass

2: Use any instead of a loop

Here we want to check if there is at least one positive element in a list. The longer solution is to loop over all numbers, check the current number, and then break once the condition is True. But for this task there exists a dedicated method in Python, the any function. Any returns True if any element of the iterable is true. This is much shorter and more pythonic than manually looping.

numbers = [-1, -2, -4, 0, 3, -7]
has_positives = False
for n in numbers:
    if n > 0:
        has_positives = True
        break

# -> refactor
has_positives = any(n > 0 for n in numbers)

3: Pull statements out of for/while loops

A lot of times you see loops where a variable is defined inside the loop, but it never changes. These are unnecessary operations, so just pull it out of the loop and then you only have to create it once.

for building in buildings:
    city = 'London'
    addresses.append(building.street_address, city)

# -> refactor
city = 'London'
for building in buildings:
    addresses.append(building.street_address, city)

4: Remove inline variables that are only used once and are immediately returned

A lot of times you see code where a variable is defined inside a function at the end, and one line later it is immediately returned. If it’s clear what the function is doing, just return the result directly. This is more concise and avoids an unnecessessary variable. However, it can still be helpful sometimes if it’s not exactly clear what the function is doing, and then you can give your last variable a meaningful name and use it as self-documenting code.

def state_attributes(self):
    """Return the state attributes."""
    state_attr = {
        ATTR_CODE_FORMAT: self.code_format,
        ATTR_CHANGED_BY: self.changed_by,
    }
    return state_attr

# -> refactor
def state_attributes(self):
    """Return the state attributes."""
    return {
        ATTR_CODE_FORMAT: self.code_format,
        ATTR_CHANGED_BY: self.changed_by,
    }

5: Replace if statement with if expression

Instead of using the if else statement to set the value of a variable, you can just set this in one line with the if expression like so. This refactoring technique is a little bit debatable, though. Some people still prefer the first option and this is just fine.

if condition:
    x = 1
else:
    x = 2

# -> refactor
x = 1 if condition else 2

6: Add a guard clause

When looking at this code it’s hard to quickly grasp what’s going on. There are multiple if-else statements and multiple indentations. Once you look closer you might realize that the first if statement covers almost the whole function code, only at the end we have the corresponding else clause where we just return False.

We can take this else clause and move it to the very beginning. This is also known as a guard statement. So if the condition is not true, we don’t execute the rest of the function code. This way wet got rid of one else clause and now there is one less level of indentation in the whole code. This looks much cleaner and is easier to understand.

def should_i_wear_this_hat(self, hat):
    if isinstance(hat, Hat):
        current_fashion = get_fashion()
        weather_outside = self.look_out_of_window()
        is_stylish = self.evaluate_style(hat, current_fashion)
        if weather_outside.is_raining:
            print("Damn.")
            return True
        else:
            print("Great.")
            return is_stylish
    else:
        return False

# -> refactor
def should_i_wear_this_hat(self, hat):
    if not isinstance(hat, Hat):
        return False

    current_fashion = get_fashion()
    weather_outside = self.look_out_of_window()
    is_stylish = self.evaluate_style(hat, current_fashion)
    if weather_outside.is_raining:
        print("Damn.")
        return True
    else:
        print("Great.")
        return is_stylish

7: Move assignments closer to their usage

This is the improved code from the last example, but it still takes a few moments to understand what’s happening here. So we want to check if we should wear a hat or not. The logic is this: If it’s raining, we always say True, and if it’s not raining, we say True if the hat is stylish. One easy way how we can drastically improve the readability of this logic is by moving the assignments closer to it’s usage. Let’s get the weather right before using the if statement. And now the fashion and the style variables are only needed in the else clause, so move them down. Now it should be a lot clearer what’s going on.

Remember my tip number 4? We could further shorten the code and immediately return the evaluate style result. However, in this example I also like the is_stylish name because it let’s you know that if the hat is stylish you say True, and otherwise False. So here it’s fine to leave the extra variable.

def should_i_wear_this_hat(self, hat):
    if not isinstance(hat, Hat):
        return False

    current_fashion = get_fashion()
    weather_outside = self.look_out_of_window()
    is_stylish = self.evaluate_style(hat, current_fashion)
    if weather_outside.is_raining:
        print("Damn.")
        return True
    else:
        print("Great.")
        return is_stylish

# -> refactor
def should_i_wear_this_hat(self, hat):
    if not isinstance(hat, Hat):
        return False

    weather_outside = self.look_out_of_window()
    if weather_outside.is_raining:
        print("Damn.")
        return True
    else:
        print("Great.")
        current_fashion = get_fashion()
        return self.evaluate_style(hat, current_fashion)
        # is_stylish = self.evaluate_style(hat, current_fashion)
        # return is_stylish

8: Simplify sequence checks

This is another thing I see very often. When you need to check if there are elements in a collection, for example in a list, you don’t need to write if len(your_list) > 0. You can simply say if your_list. This is the pep 8 recommended way and is also known as Truth Value Testing. It is possible because in Python, empty seqeuences and collections evaluate to False. So this can be applied to strings, tuples, lists, dictionaries, and sets.

if len(list_of_hats) > 0:
    hat_to_wear = choose_hat(list_of_hats)

# -> refactor
if list_of_hats:
    hat_to_wear = choose_hat(list_of_hats)

Bonus: Free VS Code/PyCharm Refactoring Extension:

There exists an extension for VS Code and PyCharm that helps you identify these refactoring patterns. This extension is Sourcery. Sourcery is a free extension that you can easily install and which then gives you helpful refactoring hints. You can install and test it here: Sourcery.ai *

* Note: That's an affiliate link 😉

Resources