One of the biggest tenants of programming in Python is writing code that's as understandable, but as concise as possible. It's so important that we have the term Pythonic to describe code that's written this way.
But we all sometimes fall into bad habits when we're in a rush to release new features and sometimes our code gets a bit... messy. Here are 30 ways you can think about refactoring your code to make it easier to understand, easier to work with, and just all around a bit more Pythonic.
1) Merge your nested if conditions
Too much nesting in your code can make it tricky to understand. Especially in Python where we don't use brackets to help out with delineation of different levels of nesting. We can easily reduce the levels of nesting we have to deal with by combining two or more
if
conditions with an and
.if a:
if b:
return c
Can easily be condensed down to
if a and b:
return c
2) Don't repeat yourself! Hoist duplicate code out of conditionals
We want to avoid duplicate code where possible and one of the most common places this crops up is within conditionals. If code is repeated in both/all branches of a conditional it's always going to execute, so we can hoist it out of the conditional and only used once.
if sold > DISCOUNT_AMOUNT:
total = sold * DISCOUNT_PRICE
label = f'Total: {total}'
else:
total = sold * PRICE
label = f'Total: {total}'
The
label = f'Total: {total}'
is getting used no matter what. So we can just pop it out of the conditional altogether. And we get the added bonus that it's now going to be clearer what the conditional actually controls for.if sold > DISCOUNT_AMOUNT:
total = sold * DISCOUNT_PRICE
else:
total = sold * PRICE
label = f'Total: {total}'
3) Fewer loops - part 1. Use any() instead of for loop
We'll often want to check whether or not a condition holds for one or every item in a collection. Usually the way we see people go about this is with a for loop:
found = False
for thing in things:
if thing == other_thing:
found = True
break
This gets the job done, but there's a much much more concise way to do it! Python has built-in
any()
and all()
functions that can easily check if a single element evaluates to True
or if every element evaluates to True
. So we can refactor this example to:found = any(thing == other_thing for thing in things)
4) Keep your loops clean. Hoist statements out of for/while loops
Loops are almost always complex and tricky to follow. Adding in additional information to a loop that doesn't need to be there just makes the loop a bit more complex - so hoisting it outside the loop is a great opportunity to make things clearer!
for building in buildings:
city = 'London'
addresses.append(building.street_address, city)
Here the
city
variable is assigned in the loop. But, it's only read, not altered at all - so we can pull it out of the loop altogether.city = 'London'
for building in buildings:
addresses.append(building.street_address, city)
5) Fewer loops - part 2. Using list/set/dictionary comprehensions
Another common task we find ourselves doing often when programming is creating a collection of values. In most languages we could do something like:
cubes = []
for i in range(20):
cubes.append(i ** 3)
We define an empty list
cubes
and then iteratively fill it with our values. This isn't wrong in Python, but there's a much nicer way to do it using built-in comprehensions. They let us simplify the entire list creation into a single line and cut out the need to define the empty list. Our example would become:cubes = [i ** 3 for i in range(20)]
Three lines down into one line is a nice win. Plus once you get used to the list comprehension syntax it actually is much more readable than the looped method.
6) Quick wins. Augmented expressions
Whenever we're dealing with updating the value of a variable relative to itself we can get a nice little simplification by using Python's expressions. Instead of:
count = count + other_value
We can just write:
count += other_value
It's not a huge change, but it's a bit shorter and more concise. Plus we only have to think about the
count
variable once. We can also use it with a bunch of other operators - -=
, *=
, /=
, and **=
.7) If expressions instead of if statements
Another common scenario we'll all deal with is looking to set a variable to one of two different values depending on some state. For example:
if condition:
x = 1
else:
x = 2
With an if expression we can cut that down to a single line:
x = 1 if condition else 2
This refactoring is a bit conditional because some developers don't find if expressions clearer than if statements. But in general I think that if the if expression is short and fits on one line then it's a definite improvement. But feel free to debate me on this one :).
8) A better loop counter - enumerate
When we're iterating over a list we often want a counter to let us know the index of the current item. Usually we'll see them written manually like:
i = 0
for currency in currencies:
print(i, currency)
i += 1
But, Python has a built in function,
enumerate
, that lets us generate the index automatically & lets us save two lines of code:for i, currency in enumerate(currencies):
print(i, currency)
9) Fewer loops - part 3. Using sums instead of loops
So often we'll have to add up a list of things. Thankfully Python has a built-in
sum()
function specifically for this. So instead of:total = 0
for hat in hats:
total += hat.price
We can just write:
total = sum(hat.price for hat in hats)
Not only is it shorter (which we love) but it's also more descriptive about what the line of code is actually doing - finding the sum of the price of hats.
10) Another quick win. Simplifying sequence comparisons
Before we do something to a list or sequence we often want to check if there are any elements there. One way it's often done is by checking if the length of the list is greater than 0:
if len(list_of_hats) > 0:
hat_to_wear = choose_hat(list_of_hats)
But Python automatically evaluates lists and sequences to
True
if they have any elements and False
if they don't. This means the more Pythonic way of doing things is:if len(list_of_hats):
hat_to_wear = choose_hat(list_of_hats)
It's a bit less cluttered to do things this way, plus it's following the conventions set out in Python's PEP8 guidelines.
There are tons of different ways to make code more Pythonic and easier to work with, these are just some of the most common examples we come across at Sourcery. This is a consolidated list of our broader refactorings series we first published on our blog. Let me know if you disagree with any of these - or if there are any other common refactorings you think are really important!