User input validation is annoying, right? My instinct is to ask fewer
questions. Does the user really need to ask for hex or binary? For example, we
could just print both conversions. And if it's a command-line script, couldn't
the decimal number come directly from the command line? Or even a bunch of
decimal numbers and our script could convert them all.
But if we retain your current approach, the repetitiveness of the validation
suggests the need to move code into functions and to create a single function
to handle all input()
calls. At a minimum, that function needs a user-prompt
and a function (or callable) that will convert the value and raise an error if
it's invalid. For example, to validate conversion type, we need to lowercase
the input and then make sure it contains a b
or h
. While we're at it, we
could simplify downstream logic by defining constants for conv_type
.
Subsequent code can use strict equality to drive logic (rather than
looser in
checks). And in this case, the constant can also serve as a label
when printing.
A consolidated approach to getting user input allows you to address
another issue: how does the user quit the program? However you
decide to handle that, it's handy to be able to do it in one place
rather than multiple.
If you strip off the 0b
prefix for binary shouldn't you do the same for hex?
If so, that simplifies the logic, because we can extract the things that vary
into a data structure (converters
) and then use that structure to drive the
logic. The result is that most of the "algorithm" in the main()
function
fades away and we end up with very little conditional branching. Instead, it
just marches step by step, calling utility functions or accessing data
structures directly. Instead, the algorithmic complexity is delegated to
smaller and more narrowly focused utility functions that are easier to think
about in isolation.
import sys
HEX = 'hexadecimal'
BIN = 'binary'
def main(args):
# Set up conversion functions and prefixes.
converters = {
HEX: (hex, '0x'),
BIN: (bin, '0b'),
}
# Perform conversions.
while True:
# Get validated input. Since int() will raise if given
# a bad input, we get that validation for free.
conv_type = get_user_input('Hexadecimal or binary', is_conv_type)
dec_num = get_user_input('Decimal number', int)
# Convert.
func, prefix = converters[conv_type]
converted = func(dec_num).replace(prefix, '')
# Report.
print(f' {converted} = in {conv_type}')
def get_user_input(prompt, validate):
while True:
# Get input and provide a way for user to quit easily.
reply = input(prompt + ': ').lower().strip()
if not reply:
sys.exit()
# Return valid converted input or nag the user.
try:
return validate(reply)
except ValueError as e:
print(f'Invalid input: {reply}')
def is_conv_type(reply):
# Takes user input. Returns HEX or BIN if valid.
reply = reply.lower()
for conv_type in (HEX, BIN):
if conv_type[0] in reply:
return conv_type
raise ValueError()
if __name__ == '__main__':
main(sys.argv[1:])