Рефакторинг на python
Написал следующий код:
print ("--------------")
print ("--Calculator--")
print ("--------------\n")
print("Enter (Q)uit to exit.\n \n")
while True:
que = input("Select an operation (+, -, *, /): ")
if que == "+":
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
print("The answer is " , num_1 + num_2, "\n")
except ValueError:
print("ValueError. Try again.\n")
elif que == "-":
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
print("The answer is " , num_1 - num_2, "\n")
except ValueError:
print("ValueError. Try again.\n")
elif que == "*":
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
print("The answer is " , num_1 * num_2, "\n")
except ValueError:
print("ValueError. Try again.\n")
elif que == "/":
try:
num_1 = float(input("Enter first number: "))
if num_1 == 0:
print("Cannot be divided by zero. Try again.\n")
else:
num_2 = float(input("Enter second number: "))
if num_2 == 0:
print("Cannot be divided by zero. Try again.\n")
else:
print("The answer is: " , num_1 / num_2, "\n")
except ValueError:
print("ValueError. Try again.\n")
elif que == "Q":
break
else:
print("Unknown value. Try again.\n")
Нетрудно заметить, что блоки постоянно повторяются. Не могу придумать, как запихнуть в функции, т.е. какие аргументы задать, чтобы менялся арифметический оператор между num_1 и num_2. Зашёл в тупик, мне нужна помощь.
Ответы (4 шт):
Нужно блок try/except поставить до if'ов:
print ("--------------")
print ("--Calculator--")
print ("--------------\n")
print("Enter (Q)uit to exit.\n \n")
while True:
que = input("Select an operation (+, -, *, /): ")
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
except:
print("ValueError. Try again")
continue
if que == "+":
print("The answer is " , num_1 + num_2, "\n")
elif que == "-":
print("The answer is " , num_1 - num_2, "\n")
elif que == "*":
print("The answer is " , num_1 + num_2, "\n")
elif que == "/":
if num_2 == 0:
print("Cannot be divided by zero. Try again.\n")
else:
print("The answer is: " , num_1 / num_2, "\n")
elif que == "Q":
break
else:
print("Unknown value. Try again.\n")
Обязательно вынести непосредственно вычисления в отдельную функцию:
def calc(a, b, op):
if op == '+':
return a + b
if op == '-':
return a - b
if op == '*':
return a * b
if op == '/':
if b == 0:
raise ZeroDivisionError('Cannot be divided by zero. Try again.')
return a / b
raise RuntimeError(f'Unknown operation {op}.')
while True:
que = input("Select an operation (+, -, *, /): ")
if que == "Q":
break
if que not in ('+', '-', '*', '/'):
print(f'Unknown operation {que}. Try again.')
continue
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
result = calc(num_1, num_2, que)
print(f'The answer is {result}')
except Exception as e:
print(e.message if hasattr(e, 'message') else e)
За корректность всего не ручаюсь, не проверял
import operator
OPERATORS = {
'+': operator.add, '-': operator.sub,
'*': operator.mul, '%': operator.mod,
'/': operator.truediv,
}
OPERATORS_STR = ', '.join(OPERATORS.keys())
def main():
print("--------------")
print("--Calculator--")
print("--------------\n")
print("Enter (Q)uit to exit.\n \n")
while True:
task = input(f"Select an operation ({OPERATORS_STR}): ")
if task in OPERATORS:
try:
num_1 = float(input("Enter first number: "))
num_2 = float(input("Enter second number: "))
result = OPERATORS[task](num_1, num_2)
print(f"The answer is {result}\n")
except ZeroDivisionError:
print("Cannot be divided by zero. Try again.\n")
except ValueError:
print("ValueError. Try again.\n")
elif task == "Q":
break
else:
print("Unknown value. Try again.\n")
if __name__ == '__main__':
main()
Улучшения:
- Поправил число пустых строк и число пробелов для улучшения стиля кода и сответствия рекомендациям PEP.
- Вынес всю логику в отдельную функцию, которая вызывается при запуске конкретно этого файла (
__name__ == '__main__'). - Вынес операторы и работу с ними в словарь. Использовал стандартный модуль операторов, можно также добавить кастомные функции или лямбы. Добавил ещё один оператор, чтобы показать, как теперь это легко.
- Список операторов теперь строится автоматически – меньше дублирования кода.
- Улучшил работу со строками используя f-строки из Python 3.
- Упростил обработку деления на ноль – тот же код сработает и для взятия модуля.
За корректность ручаюсь, проверял
Код немного непонятный, но хорошо расширяемый. И еще... кажись я опоздал. Любая функция, соответствующая типу таска принимает в качестве аргумента строку таска. Иногда она игнорируется, но мы избавляемся от сложной логики, просто вызвав функцию для таска вслепую.
import operator
OPERATORS = {
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'%': operator.mod,
'/': operator.truediv,
}
INFO_WIDTH = 30
TEXTS = {
'title': "Calculator",
'quit': 'Enter (Q)uit to exit.',
'task input': f"Select an operation ({', '.join(OPERATORS.keys())}): ",
'num input': "Enter {} number: ",
'ans': "The answer is {}\n",
'zero err': "Cannot be divided by zero. Try again.\n",
'value err': "ValueError. Try again.\n",
'unklown': "Unknown value. Try again.\n"
}
def calc(task):
try:
num_1 = float(input(TEXTS['num input'].format('first')))
num_2 = float(input(TEXTS['num input'].format('second')))
result = OPERATORS[task](num_1, num_2)
print(TEXTS['ans'].format(result))
except ZeroDivisionError:
print(TEXTS['zero err'])
except ValueError:
print(TEXTS['value err'])
TASKS = {
lambda x: x in OPERATORS: calc,
lambda x: x == 'Q': lambda x: quit()
}
def line(text=''):
char = ['-' for _ in range(INFO_WIDTH)]
pad_len = (INFO_WIDTH - len(text)) // 2
char[pad_len: pad_len + len(text)] = list(text)
print(''.join(char))
def main():
line()
line(TEXTS['title'])
line(TEXTS['quit'])
line()
print()
while 1:
task = input(TEXTS['task input'])
for _ in TASKS:
if _(task):
TASKS[_](task)
break
else:
print(TEXTS['unklown'])
if __name__ == '__main__':
main()