This post is to talk about the three most common mistakes I saw over the last week and some possible alternatives that might be better! So let’s start the countdown!

# Calculating whether or not 'year' is a leap yearif year % 4 == 0:

if year % 100 == 0:

if year % 400 == 0:

return True

else:

return False

else:

return True

else:

return False

A lot of times, I’ll pull a line from the Zen of Python to lead off my feedback to a “mentee” (not to be confused with a manitee). When I see this issue, I always lead with

Flat is better than nested.

If you look at your code with your eyes unfocused, looking at the shapes and not reading the words, and you see a bunch of arrows going out and back in again:

/

/

/

/

/

/

/

/

/

It’s not *definitely* a bad thing, but it is a “code smell,” or a Spidey Sense that something could possibly be refactored.

So, what can you do instead of nest? There are a couple things to try. The first is inverting your logic and using “early returns” to peel off small pieces of the solution space one at a time.

if year % 400 == 0:

return True

if year % 100 == 0:

return False

if year % 4 == 0:

return True

return False

If the number is divisible by 400, then we immediately return true. Otherwise, for the rest of our code, we can know that year is *definitely not* divisible by 400. So, at that point, any other year that’s divisible by 100 is not a leap year. So we peel off that layer of the onion by returning False.

After that, we can know that `year`

is definitely not a multiple of 400 *or* 100, and the remainder of the code follows the same pattern.

The other way to avoid nesting is by using “boolean operators:” `and, or, and not`

. We can combine `if`

statements and thus, save ourselves a layer of nesting!

if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):

return True

else:

return False

Of course, that leads us to our second item…

We’ll start with our last example from above:

if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):

return True

else:

return False

Anytime you find yourself writing:

if something:

return True

else:

return False

You should remember that the clause of an `if`

statement is itself a boolean!

>>> year = 2000

>>> year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)

True

So, why not type a little less and return the result of the boolean operation directly?

return (year % 4 == 0 and (year % 100 != 0 or year % 400 == 0))

Granted, at this point, the line may be getting a little long, but the code is a little less redundant now!

Here are two possible ways that this could show up:

some_numbers = [1, 2, 5, 7, 8, …]

other_numbers = [1, 3, 6, 7, 9, …]## Let’s try to combine these two without duplicates

for number in other_numbers:

if number not in some_numbers:

some_numbers.append(number)

Or:

data = [[“apple”, 4], [“banana”, 2], [“grape”, 14]]## What fruits do we have?

for item in data:

print(item[0])## => “apple” “banana” “grape”

## How many grapes do we have?

for item in data:

if item[0] == “grape”:

print(item[1])## => 14

In the first case, you’re trying to keep track of some groups of items and you want to combine them without duplicates. This is an *ideal* candidate for a `set`

. Sets inherently keep track of their items (although not the order, so don’t use a set if the order is important). You can declare them with the built-in `set()`

function or with squiggle braces (`{}`

).

some_numbers = {1, 2, 5, 7, 8}

other_numbers = {1, 3, 6, 7, 9}## Sets use the ‘binary or’ operator to do “unions”

## which is where they take all of the unique elements

some_numbers | other_numbers

## => {1, 2, 3, 5, 6, 7, 8, 9}

## You can even add single items in!

some_numbers.add(10)

## => {1, 2, 5, 7, 8, 10}

## But adding a duplicate doesn’t change anything

some_numbers.add(1)

## => {1, 2, 5, 7, 8, 10}

In the second case, again, order probably isn’t critical. You want to keep track of some data by a “label” or something, but be able to keep them all together and list them out as necessary. This time, you’re probably looking for a `dict`

. You can create those with either the `dict()`

built-in function or, again, squiggle braces (`{}`

). This time, however, you separate the labels (keys) and the values with a colon.

fruits = {

“apples”: 4,

“bananas”: 2,

“grapes”: 14,

}

You can list out all of the keys (or values!).

list(fruits.keys())## => [“apples”, “bananas”, “grapes”]

list(fruits.values())

## => [4, 2, 14]

## Or both!

list(fruits.items())

## => [(“apples”, 4), (“bananas”, 2), (“grapes”, 14)]

And you can ask it about (or give it a new value for) specific keys.

# How many grapes are there?

fruits[“grapes”]## => 14

## Not anymore. I ate some.

fruits[“grapes”] = 0

fruits[“grapes”]

## => 0

Using a list, the your algorithm loops through every item to find the right one. `dict`

's are built to have very fast lookups, so, even if your `dict`

is a bazillion fruits long, finding the `grapes`

is still super fast – and easy to type! No loops!

Thanks for reading ❤

If you liked this post, share it with all of your programming buddies!

Follow me on **Facebook** | **Twitter**

☞ Complete Python Bootcamp: Go from zero to hero in Python 3

☞ Machine Learning A-Z™: Hands-On Python & R In Data Science

☞ Python and Django Full Stack Web Developer Bootcamp

☞ An A-Z of useful Python tricks

☞ Top 100 Python Interview Questions and Answers

☞ A guide to Face Detection in Python

☞ Python Web Scraping Tutorial

☞ Top 5 Python Frameworks For Test Automation In 2019

☞ Python Tutorial for Beginners (2019) - Learn Python for Machine Learning and Web Development

#python

17.10 GEEK