Простая программа-калькулятор, работающая с несколькими входами

Это моя третья версия построения простого калькулятора Python. Первые две версии работают только с двумя входами для выполнения вычислений, эта версия допускает несколько входов. Однако результат оставит .0. Могу ли я как-нибудь еще улучшить эту программу, кроме этого? Я также включил строки документации, чтобы объяснить функцию каждого блока кода.

import operator
from functools import reduce
import sys

# Display a list of math operators that the user can choose to perform their calculation
MATH_OPERATIONS = """nnList of math operations:

1. Addition (+)

2. Subtraction (-)

3. Multiplication (*)

4. Division (/)

5. Exponentiation (**)

"""


# A dictionary that contain options of operations to perform the calculation
OPERATIONS = {
    1: operator.add,
    2: operator.sub,
    3: operator.mul,
    4: operator.truediv,
    5: operator.pow
}


def ask_user_yes_no(yes_no_question):
    """
    Simplifies if/else in determining the correct answers from the user input.
    Returns True if the user answer the prompt with any of the values in choice_yes.
    Returns False if the user enters any of the values in choice_no
    """
    
    choice_yes = ["yes", 'y']
    choice_no = ["no", 'n']

    while True:
        user_choice = input(yes_no_question).lower()

        if user_choice in choice_yes:
            return True
        elif user_choice in choice_no:
            return False
        else:
            print("nnInvalid Input. Try again.")


def count_number_input():
    """
    Takes a user input and count the amount of numbers that the user wants to calculate.

    Prints out a message if ValueError occurs.
    """

    while True:
        try:
            count_input = int(input("nHow many number would you like to calculate?: "))
        except ValueError:
            print("nINVALID INPUT - Field must not be blank or contained non-integer or non-numerical values.")
        else:
            return count_input


def get_number_list():
    """
    Calls count_number_input function to get how many numbers that the user wants to calculate.

    Iterates over the range of the elements in the function and then
    asks the user to input the number that they would want to calculate.

    Prints out messages if a ValueError occurs.
    """

    input_amount = count_number_input()

    while True:
        try:
            numbers_list = [float(input("nNumbers: ")) for _ in range(input_amount)]
        except ValueError:
            print("nInvalid input, try again.")
            print("nPlease ensure that the prompt does not contain a null or non-integer or non-numerical values.")
        else:
            return numbers_list


def select_choice():
    """
    Prints out a list of math operations.
    Asks the user to select an option to use in the calculation.
    Check user's selection for ValueError and skip if none is found.
    Prints out a message if the user has selected an option beyond the specified range of options.
    """

    print(MATH_OPERATIONS)

    while True:
        try:
            user_choice = int(input("Select an option | 1 | 2 | 3 | 4 | 5 |: "))          
        except ValueError:
            print("nINVALID INPUT - Field must not be blank or contained non-integer or non-numerical values.n")
            continue
        
        if user_choice > 5:
            print("nOption selected must be from 1 to 5 only!n")
        else:
            return user_choice


def calculate(numbers, choice):
    """
    Applies operations across all numbers and return the result.

    Calculation:

    >>> calculate([2, 2], 1)
    4.0

    >>> calculate([5, 3], 2)
    2.0

    >>> calculate([5, 5] 3)
    25.0

    >>> calculate([9, 3] 4)
    3.0

    >>> calculate([4, 4] 5)
    256.0
    """

    return reduce(OPERATIONS[choice], numbers)


def start_program():
    """
    Starts the program by asking the user the amount of numbers that they want to calculate,
    and then perform a calculation on those numbers.

    Prints out the result of calculation.
    """

    user_number = get_number_list()

    user_choice = select_choice()

    print("nResult: ", calculate(user_number, user_choice))


def should_calculate_again():
    """
    Calls start_program function to run the program first.

    Asks the user if they want to perform more calculation.

    Restarts the program if ask_user_yes_no returns True.
    Exits the program telling the user that the program has exited
    if ask_user_yes_no returns False.

    """

    while True:

        start_program()

        if not ask_user_yes_no("nnWould you like to perform more calculation? (Y/N): "):
            sys.exit("nn-----Program Exited-----n")


should_calculate_again()

1 ответ
1

Док-тест

Похоже, вы намеревались включить в свой """docstrings""", но похоже, что вы их не использовали.

Запуск тестов …

>>> import doctest
>>> doctest.testmod()

… показывает, что все 5 тестов не пройдены!

Сбои теста (неправильный вывод)

Целочисленный ввод дает целочисленный вывод, а не с плавающей запятой:

Failed example:
    calculate([2, 2], 1)
Expected:
    4.0
Got:
    4
Failed example:
    calculate([5, 3], 2)
Expected:
    2.0
Got:
    2

Сбои теста (синтаксические ошибки)

В этих примерах отсутствуют запятые:

Failed example:
    calculate([5, 5] 3)
Exception raised:
    Traceback (most recent call last):
      File "C:Program FilesPython39libdoctest.py", line 1336, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.calculate[2]>", line 1
        calculate([5, 5] 3)
                         ^
    SyntaxError: invalid syntax
Failed example:
    calculate([9, 3] 4)
Exception raised:
    Traceback (most recent call last):
      File "C:Program FilesPython39libdoctest.py", line 1336, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.calculate[3]>", line 1
        calculate([9, 3] 4)
                         ^
    SyntaxError: invalid syntax
Failed example:
    calculate([4, 4] 5)
Exception raised:
    Traceback (most recent call last):
      File "C:Program FilesPython39libdoctest.py", line 1336, in __run
        exec(compile(example.source, filename, "single",
      File "<doctest __main__.calculate[4]>", line 1
        calculate([4, 4] 5)
                         ^
    SyntaxError: invalid syntax

Убийство переводчика

Если вы добавите звонок в doctest.testmod() после звонка should_calculate_again(), вы обнаружите, что доктесты никогда не запускаются. Причина: sys.exit() убивает переводчика. Любые тесты, которые вы собираетесь запустить, не могут быть запущены, или у них никогда не будет возможности сообщить об их окончательном статусе.

Изменение sys.exit(...) к print(...) а также break операторы завершат цикл while, а should_calculate_again() вернется нормально.

TL; DR: Вам почти никогда не нужно или не хочется звонить sys.exit().

  • Спасибо за ваш отзыв. Я не знал о doctest модуль, я думал, что строка документации просто объясняет, что делает код. Итак, для неправильной части вывода я все еще работаю над возвращением правильного типа данных на основе ввода пользователя. Я исправил синтаксические ошибки и изменил sys.exit() к тем, которые вы предоставили. И о sys.выход(), would it still be the same if I just use exit () `?

    — Королевская лягушка

  • Строка документации описывает, как использовать функцию. После импорта модуля введите help(calculate) в командной строке Python предоставит пользователю справочную информацию. Включение примеров того, как функция ведет себя или используется, является отличным дополнением к документации. В качестве бонуса его можно использовать в качестве встроенного модульного теста через doctest модуль. Я бы не стал включать каждый последний тест в строку документации — это делает текст справки слишком длинным и нечитаемым. Для полного покрытия кода следует использовать внешние модульные тесты. Но тесты, которые вы включили в текст справки, должны пройти.

    — А.Дж.Нойфельд

  • Мой тест exit(message) а также sys.exit(message) показать такое же поведение. Одно, вероятно, реализовано с точки зрения другого. Дело не в том, чтобы вызывать любую из этих функций. Вместо этого позвольте сценарию естественным образом завершиться, вернувшись из последней вызванной функции и достигнув конца сценария верхнего уровня. Рассмотрим модульный тест, написанный на Python, который устанавливает для стандартного ввода значение "2n2.0n3.03nNon", захватывает стандартный вывод, звонки should_calculate_again() и пытается проверить, что выводится правильный. Это не пройдет и не завершится ошибкой, потому что интерпретатор Python завершился.

    — А.Дж.Нойфельд

  • Хм, еще новичок в этом doctest модуль. Итак, сколько это слишком много, чтобы включать каждый тест в строку документации? Возможно, я запутался, думая, что строка документации предназначена только для объяснения кода, потому что я видел так много примеров, объясняющих функцию. Что касается вашего примера модульного теста, я не уверен, что вы имеете в виду под словом «ни годен, ни провал». Вы получите свой результат до того, как программа спросит, хотите вы продолжить или нет.

    — Королевская лягушка


  • «Слишком много» — определенно серая зона, открытая для интерпретации. «Первые две версии работают только с двумя входами для выполнения расчетов, эта версия допускает несколько входов». Что ж, возможно, будет уместным пример, показывающий более двух входов. Другие тесты могут быть хороши для юнит-теста, но не так важны для включения в help(calculate) документация: деление на ноль, операции с бесконечностями и числами, отличными от числа, или квадратный корень из отрицательных чисел. Последние операции важны для тестирования и могут потребовать комментария в тексте справки, но полный пример может оказаться слишком большим.

    — А.Дж.Нойфельд

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *