Reportedly a high percentage of programmer applicants can’t solve this quickly.
Write a program that prints the numbers from 1 to 100. But for multiples of three print “Fizz” instead of the number and for the multiples of five print “Buzz”. For numbers which are multiples of both three and five print “FizzBuzz”.
A deep dive on this problem has been done in jest many times, e.g., deliberate over-engineering or code golf. But in all seriousness, let’s consider what’s the most Pythonic solution. A truncated version of the common solution:
for num inrange(1, 16):if num %5==0and num %3==0:print('FizzBuzz')elif num %3==0:print('Fizz')elif num %5==0:print('Buzz')else:print(num)
Naturally interview questions tend to focus on output, e.g. print, but that’s no reason to skip over basic abstractions or data structures. First, this could be written as a generator, to decouple the print operation and parametrize the numeric range. Alternatively, Python has such strong iterator support that it could also be just a function, ready to be mapped. So let’s reframe the basic solution as:
def fizzbuzz(stop):for num inrange(1, stop):if num %5==0and num %3==0:yield'FizzBuzz'elif num %3==0:yield'Fizz'elif num %5==0:yield'Buzz'else: yieldstr(num)' '.join(fizzbuzz(16))
Even at this size, it’s already violating DRY, or the Rule of 3. Clearly the same logic is being repeated with different data.
def fizzbuzz(stop): items = (15, 'FizzBuzz'), (3, 'Fizz'), (5, 'Buzz')for num inrange(1, stop):yieldnext((text for div, text in items if num % div ==0), str(num))' '.join(fizzbuzz(16))
However, that variation had to introduce the concept of the least common multiple. Even in such a trivial problem, there’s a subtlety in how one interprets requirements. The final directive to output “FizzBuzz” can be seen as a mere clarification of the previous directives; certainly not a coincidence. Making this the more obvious solution:
def fizzbuzz(stop):for num inrange(1, stop): text =''if num %3==0: text +='Fizz'if num %5==0: text +='Buzz'yield text orstr(num)' '.join(fizzbuzz(16))
Arguably that insight is more important, because its duplication grows exponentially, not linearly. There’s a 2**N sized case statement to handle N cases, luckily N == 2. Adding just one more directive for the number 7 would make the basic solution unwieldy.
And of course both approaches can be combined.
def fizzbuzz(stop): items = (3, 'Fizz'), (5, 'Buzz')for num inrange(1, stop):yield''.join(text for div, text in items if num % div ==0) orstr(num)' '.join(fizzbuzz(16))
So is that over-engineered? This author would argue that both deduplication and decoupling logic from data are worth observing. So maybe at this size the final version isn’t necessary, but surely the basic version is not the most Pythonic.