In my last post, I talked about how using coding challenges, or katas, can help you grow as a python developer.
In particular, I mentioned how the Fizz Buzz challenge enabled me to think about the “O” in the SOLID Principles. In this article, I will explain how I came to my solution.
To quickly recap my intention, I wanted to create a solution that would print “fizz” for every number divisible by three. I would then expand its capabilities to include “buzz” and “fizzbuzz” without changing the original code.
I quickly realized that I needed to split the responsibilities between multiple components. I needed:
- something to generate numbers
- something to process numbers with the intention of solving the “fizz” part of the problem
- something to coordinate the logic of processing the numbers
I decided to take the first and third needs together and created a function that would take two arguments, the number to count to, and a list of functions to process each number.
def count_and_process(count_to: int, funcs: List[Callable]): pass
Generating the numbers is easy in python. I created a
range object that would generate each number needed, and I set that number to a variable in a for loop:
def count_and_process(count_to: int, funcs: List[Callable]): for i in range(1, count_to + 1): ...
Next, I had to solve a tricky part. Since I am receiving a list of functions to call to process each number, I felt like I needed a way to pass it two pieces of state: the current number, and the result from every other function that proceeded it. I decided to do that with tuples. The first element of the tuple would be the current number. The second element is the processed result.
In my mind, I could see a stream of data that would look like this:
# starting -> processed by "fizz" -> processed by "buzz" (1, 1) (1, 1) (1, 1) (2, 2) (2, 2) (2, 2) (3, 3) (3, 'fizz') (3, 'fizz') (4, 4) (4, 4) (4, 4) (5, 5) (5, 5) (5, 'buzz') (6, 6) (6, 'fizz') (6, 'fizz') (7, 7) (7, 7) (7, 7) (8, 8) (8, 8) (8, 8) (9, 9) (9, 'fizz') (9, 'fizz') ...
This way, each processing function would see what the current number is, but would also have the opportunity to interact with the results from previous processing functions.
I needed to write code that would create the tuple, hand it off to the processing functions, and return the result.
def count_and_process(count_to: int, funcs: List[Callable]): for i in range(1, count_to + 1): data = (i, i) for f in funcs: data = f(data) yield data
All that is left is to create the function that would handle the processing of the numbers. My choice of using tuples to pass the data around means I need to create a new tuple whenever we need to print out something other than the number.
def fizz_3(data: Tuple[int, Union[int, str]]): if data % 3 == 0: data = (data, 'fizz') return data
To make the exercise work as intended, printing the numbers from 1–100, one would do this:
>>> print(list(count_and_process(100, [fizz_3]))) [1, 2, 'fizz', 4, 5, 'fizz', 7, 8, 'fizz', 10 ...]
This is where the rubber met the road. My previous function 'fizz’ed at every third number. Now I needed to add the “buzz”.
Thankfully, my plan worked. The hard work had already been done. I first needed to create the function that would produce the “buzz” results:
def buzz_5(data: Tuple[int, Union[int, str]]): if data % 5 == 0: if isinstance(data, str): data = (data, data + 'buzz') else: data = (data, 'buzz') return data
Then, all I had to do was hooked it into the process:
>>> print(list(count_and_process(10, [fizz_3, buzz_5]))) [1, 2, 'fizz', 4, 'buzz', 'fizz', 7, 8, 'fizz', 'buzz']
Was my solution the best ever? No. But it really helped me to think through how to structure my code.
How did you solve it? What challenges did you set for yourself? Would you want to do more of this?
I am creating a Python Growth Challenge for the month of April. The idea is to gather a group of us to practice katas like this. Each week, each of us will challenge ourselves to do something we’re not great at. I’ll provide the code kata, and we’ll meet together to discuss what we’ve each learned. We’ll do it for five weeks and see how we’ve grown.
If you’re interested, please fill out my interest form. To make sure we all can talk, I need to keep this group small, so I will need to cap it at 20 people.
But please join us, no matter your skill level!