Handle invalid input
I found an issue with the way you handle some user input:
Start Number? hello
Traceback (most recent call last):
File "python", line 7, in <module>
File "python", line 9, in fizzbuzz
ValueError: invalid literal for int() with base 10: 'hello'
Which is a consequence of this conversion to int
without checking that it actually can be converted to int
:
int(input('Start number? ')) # and others like it
You could use a while
loop along with try...except
to continue asking for input until it is usable:
input_is_valid = False
while not input_is_valid:
start_num = input('Start Number? ')
end_num =input('End Number? ')
try:
start_num = int(start_num)
end_num = int(end_num)
input_is_valid = True
except ValueError:
print('Invalid number input for start "{0}" or end "{1}".'.format(start_num, end_num))
It may also be worthwhile to consider adding another check to make sure the start number is less than the ending number to prevent confusing behavior if that were not the case.
Another user input related concern is this:
while response == 'y':
Albeit very minor, it treats capital Y
as not yes. Simple fix of course:
while response.lower() == 'y':
Your class divisor_text_pair()
(by the way, class names in Python should follow TitleCase) has no functionality beyond holding two values. Using a native namedtuple
type instead would simplify the code while still allowing you to retrieve the values with dot notation x.divisor
and x.text
:
from collections import namedtuple
DivisorTextPair = namedtuple('DivisorTextPair', 'divisor text')
class FizzBuzz(): #...
The syntax to use a namedtuple
is like that of a class:
pair = DivisorTextPair(divisor, text)
Class approach
While you class fizzbuzz()
approach can be and has been critiqued, I would like to approach it from the angle actually using your class approach, but in a better way.
Your main class is really more of a procedure, as it does not have a constructor, and a lot of the logic is directly in the body of the class, rather than in methods.
P.S.: Note that I will be using Python 3.5 type hints in the method/function signatures, which I recommend to use.
In my refactor of your class (runnable demo on repl.it), the first section would be the constructor and what happens once __init__
is called:
class FizzBuzz():
def __init__(self):
self.start_fizzbuzz()
Along with a method to control the general flow:
def start_fizzbuzz(self) -> None:
"""Controls the overall logic of a FizzBuzz program."""
start_num, end_num = self.get_start_end_numbers()
pair_list = self.get_divisor_text_pairs()
self.print_numbers_and_texts(start_num, end_num, pair_list)
Note the use of Iterable Unpacking (often referred to as "tuple unpacking") in start_num, end_num = self.get_start_end_numbers()
(that method will return a tuple
, read on). The "long" way of writing the same thing without using tuple unpacking would be like this:
start_end_nums = self.get_start_end_numbers()
start_num = start_end_nums[0]
end_num = start_end_nums[1]
From there, we are getting the start and end numbers from a method where I moved the user input logic to:
def get_start_end_numbers(self) -> tuple:
input_is_valid = False
while not input_is_valid:
start_num = input('Start Number? ')
end_num =input('End Number? ')
try:
start_num = int(start_num)
end_num = int(end_num)
input_is_valid = True
except ValueError:
print('Invalid number input for start "{0}" or end "{1}".'.format(start_num, end_num))
return start_num, end_num
The get_divisor_text_pairs()
method is what you'd expect it to be, with above applied:
def get_divisor_text_pairs(self) -> list:
pair_list = []
response = 'y'
while response.lower() == 'y':
input_is_valid = False
while not input_is_valid:
divisor = input('Divisor? ')
try:
divisor = int(divisor)
input_is_valid = True
except ValueError:
print('"{0}" is not a valid number.'.format(divisor))
text = input('Text? ')
pair = DivisorTextPair(divisor, text)
pair_list.append(pair)
response = input('Input Another Divisor (y/n)? ')
return pair_list
print_numbers_and_texts()
method is essentially unchanged (except for the type hints in the signature)