Welcome to the day 6 project for the 30 Days of Python series. Today's project is actually a very common interview question, which revolves around a childhood counting game called Fizz Buzz.
In case you're not familiar with the game, it goes like this:
- One player starts by saying the number
1
. - Each player then takes it in turns to say the next number, counting one at a time.
- If the number is divisible by
3
, instead of saying the number, the player should say, "Fizz". - If the number is divisible by
5
, instead of saying the number, the player should say, "Buzz". - If the number is divisible by
3
and5
, instead of saying the number, the player should say, "Fizz Buzz". - If you make a mistake, you're usually eliminated from the game, and the game continues until there's only a single player remaining.
If there are no mistakes, the first 15 rounds of Fizz Buzz should look like this:
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
Fizz Buzz
Below you'll find a brief explaining what to do for our version, and you'll also find a model solution with an accompanying explanation. I'd really recommend you try to do this on your own before checking out our version.
Just like with the day 3 project, there's nothing wrong with looking back at the content for the last 6 days, or referencing your notes. You also shouldn't be worried if your solution is a little different to ours, as there are many, many ways to tackle this particular problem.
The brief
For our version, we're only going to have a single player, the computer, and it's going to play the first 100 rounds of Fizz Buzz all by itself. In other words, we need to print out the first 100 items in the sequence, starting from 1
.
In order to complete this exercise, you're going to need to use loops, and you can generate your list of numbers using range
. You're also going to need conditionals, and you're going to need be able to check if something is divisible by 3
or 5
.
For this last part, you can use an operator called modulo, which uses the percent symbol (%
). Modulo will give you the remainder of a division, so if a number is divisible by 3
, the value of number % 3
will be 0
.
If you want to learn about modulo in more detail, we have a post that you can check out.
An alternative you can make use of is the is_integer
method. We can call this on a float to check if it's an integral (whole) number. For example, we can write something like this:
(2.0).is_integer() # True
(3.7).is_integer() # False
And we can test the result of a division like this:
(12 / 4).is_integer() # True
(12 / 5).is_integer() # False
Good luck!
Solution walkthrough
Just like we did for the day 3 project, we're going to break the project up into smaller chunks. This is going to make it much easier to catch our mistakes early, and it's also going to make the project a little less daunting.
If you'd prefer to watch a video walkthrough for this project, see here.
I think a good first step is just getting our range of numbers. We can this with the range
function, and we're going to need to pass in two arguments: a start value, and a stop value. We need a start value because range
will start at 0
by default.
Don't forget that the stop value for range
is not inclusive, so we need to specify a range from 1
to 101
:
numbers = range(1, 101)
We can check that we have everything we need by converting it to a list a printing it. Remember that we can't print range
directly, because range
is lazy, and doesn't calculate its values until we ask for them.
numbers = list(range(1, 101))
print(numbers)
Assuming we don't have any problems, I think the next logical step is to print the numbers using a loop. We can do away with our list and numbers
variable at this point, and just put our range
in the loop directly:
for number in range(1, 101):
print(number)
This should give us 1
to 100
printed to the console, with each number on a different line.
Now we need to start filtering out the numbers we don't want to print from this output. Let's start by accounting for Fizz numbers: those divisible by 3
.
Using the modulo approach, we can do something like this:
for number in range(1, 101):
if number % 3 == 0:
print("Fizz")
else:
print(number)
Here we check to see if a number is divisible by 3
. If it is, we print "Fizz"
; otherwise, we print the number itself. We've seen conditionals a few times now, so this is hopefully relatively straightforward at this point.
If we wanted to use the is_integer
method, the solution looks very similar:
for number in range(1, 101):
if (number / 3).is_integer():
print("Fizz")
else:
print(number)
Okay, now we that we have Fizz numbers taken care of, let's expand our conditions to account for Buzz.
The process is very similar, we just have to add an elif
clause checking for a second condition. That condition is whether or not the number is divisible by 5
.
for number in range(1, 101):
if number % 3 == 0:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
else:
print(number)
This step is a place where people often trip up. If we don't use an elif
clause, and instead use a new if
statement, we end up in a situation where we have two lots of output for many of the numbers.
Let's look at an incorrect version and think about what's going on:
for number in range(1, 101):
if number % 3 == 0:
print("Fizz")
if number % 5 == 0:
print("Buzz")
else:
print(number)
If a number is divisible by 3
, we trigger this first conditional. We then check the second condition. If the number is divisible by 5
, we end up also printing "Buzz"
on the next line. While we want this to happen, we want it all on the same line, and we're going to use another step for this. If the number isn't divisible by 5
, we end up printing "Fizz"
and the number. Make sure you don't fall into this trap.
Now that we've done "Fizz"
and "Buzz"
, we need to account for "Fizz Buzz"
. This is another place people often trip up, because the order of the conditions matters. You'll see what I mean in a second.
First, let's look at how we can do this using nested conditions:
for number in range(1, 101):
if number % 3 == 0:
if number % 5 == 0:
print("Fizz Buzz")
else:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
else:
print(number)
This solution works because for any numbers divisible by 3
, we perform a second check. If the number is divisible by 5
as well, we know that the conditions have been met for "Fizz Buzz"
, so we can print that to the console. If this second condition wasn't met, we know the number is only divisible by 3
, so we can print "Fizz"
instead.
That solution works great, but what about a solution that doesn't use these nested conditions? We have two major options, but we'll save the second one for the bonus content at the end.
The option we can use here is checking if something is divisible by 15
. That's because any number divisible by both 3
and 5
is also divisible by 15
. As I mentioned already, where we put this condition is really important. For example, if we try the following, we're not going to get the output we want:
for number in range(1, 101):
if number % 3 == 0:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
elif number % 15 == 0:
print("Fizz Buzz")
else:
print(number)
This is because Python is only going to check conditions until it finds one that's true. Any number divisible by 15
is also divisible by 3
, so this first condition catches these numbers before we hit this third branch. We therefore get "Fizz"
printed where we expect "Fizz Buzz"
.
To correct this, we need to put the more specific conditions first. In this instance, something being divisible by 3
is a broader condition than being divisible by 15
, because the numbers divisible by 15
are a smaller subset of the numbers divisible by 3
. The condition checking for divisibility by 15
therefore needs to come first.
for number in range(1, 101):
if number % 15 == 0:
print("Fizz Buzz")
elif number % 3 == 0:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
else:
print(number)
With that, both our solutions work, and we're done!
Bonus material
Dividing by 15
is a neat trick to keep the solution down to a single conditional block, but maybe it's not super clear to some people what's going on. It would maybe be better if we could be direct about what we're checking here.
We can actually evaluate multiple expressions using a pair of special Boolean operators called and
and or
. Their use in this case is relatively straightforward, and very easy to read:
for number in range(1, 101):
if number % 3 == 0 and number % 5 == 0:
print("Fizz Buzz")
elif number % 3 == 0:
print("Fizz")
elif number % 5 == 0:
print("Buzz")
else:
print(number)
However, there are some important details about how and
and or
works, so if you're interested in using these operators, you should read our post on this topic.
We also have some additional solutions to Fizz Buzz in another post. If you're interested in seeing some of these alternative approaches, you can find them here.