3
\$\begingroup\$

I want to start learning python for fun and so I could dig into machine learning projects later in the future maybe.

I started off with a small calculator project just to get the hang of the language and the IDE and wanted to get some feedback. I prefer progressing in baby steps and get some useful tips even though this is a really small project. Any comment would be great (regarding the code itself, code standards, etc)

Here's the code:

calculator.core

MULTIPLY_OPERATOR = '*'
DIVIDE_OPERATOR = '/'
SUBTRACTION_OPERATOR = '-'
PLUS_OPERATOR = '+'
INVALID_CALCULATION_CODE = -1


def summation(first_operand, second_operand):
    """
    Summing the two operands
    :param first_operand: first operand
    :param second_operand: second operand
    :return: sum of both operands, success indication
    """
    return first_operand + second_operand, True


def subtraction(first_operand, second_operand):
    """
     subtraction of second operand from the first
    :param first_operand: first operand
    :param second_operand: second operand
    :return: subtraction of second operand from the first, success indication
    """
    return first_operand - second_operand, True


def multiply(first_operand, second_operand):
    """
     multiply operands
    :param first_operand: first operand
    :param second_operand: second operand
    :return: multiplication of first and second operands, success indication
    """
    return first_operand * second_operand, True


def divide(first_operand, second_operand):
    """
    divide first operand by the second
    :param first_operand: first operand
    :param second_operand: second operand
    :return: divide result, success indication
    """
    if second_operand == 0:
        return INVALID_CALCULATION_CODE, False
    return first_operand / second_operand, True


SUPPORTED_OPERATORS_DICT = {
    "+": summation,
    "-": subtraction,
    "/": divide,
    "*": multiply
}

calculator_console

from calculator_core import *


INVALID_COMMAND_CODE = -1
EXIT_COMMAND = "exit"


def find_operator_index(command):
    """
    Returns the index of the operator in the command
    :param command: The string command from the user
    :return: The index of the operator
    """

    for charIndex in range(len(command)):
        if command[charIndex] in SUPPORTED_OPERATORS_DICT.keys():
            return charIndex

    return INVALID_COMMAND_CODE


def print_error_message(code):
    """
    prints error message
    :param code: error code
    :return:
    """

    if code == INVALID_COMMAND_CODE:
        print "The command you entered is in valid. Please try again.\n"
    elif code == INVALID_CALCULATION_CODE:
        print "The calculation is in valid. Please try again\n"


def float_try_parse(value):
    """
    gets a string and tries to convert it to int
    :param value: string to be converted
    :return: integer value and success indication
    """
    try:
        return float(value), True
    except ValueError:
        return value, False


def take_commands():
    """
    Process a command from the user and outputs the result to the console
    :return:
    """

    while True:
        command = raw_input("Please enter a command\n")

        # Exit
        if command == EXIT_COMMAND:
            return

        # Processing a computing command
        operator_index = find_operator_index(command)

        # Invalid operator index
        if operator_index == INVALID_COMMAND_CODE:
            print_error_message(INVALID_COMMAND_CODE)
            continue

        # Parsing first operand
        first_operand, success = int_try_parse(command[0:operator_index])
        if not success:
            print_error_message(INVALID_COMMAND_CODE)
            continue

        # Parsing second operand
        second_operand, success = int_try_parse(command[operator_index+1:])
        if not success:
            print_error_message(INVALID_COMMAND_CODE)
            continue

        # Executing The command
        result, success_indication = SUPPORTED_OPERATORS_DICT[command[operator_index]](first_operand, second_operand)
        if not success_indication:
            print_error_message(INVALID_CALCULATION_CODE)
        else:
            print("{}{}".format(result, '\n'))


if __name__ == '__main__':
    """
    The main function
    """

    take_commands()

calculator_gui

from tkinter import *
from calculator_core import *

fields = 'First Operand', 'Second Operand'

variables = {'curr_operator': "+"}


def float_try_parse(value):
    """
    gets a string and tries to convert it to int
    :param value: string to be converted
    :return: integer value and success indication
    """
    try:
        return float(value), True
    except ValueError:
        return value, False


def compute(entries):
    """
    Computes the result of the given computation
    :param entries: Form entries
    :return:
    """
    values = []
    for entry in entries:

        value = entry[1].get()

        value, success = float_try_parse(value)

        if not success:
            return

        values.append(value)

    result, success = SUPPORTED_OPERATORS_DICT[variables['curr_operator']](values[0], values[1])

    if not success:
        return

    result_label['text'] = result


def makeform(root, fields):
    """
    Creating the form fields
    :param root: The form root
    :param fields: Fields to be created
    :return: The created form entries
    """
    entries = []
    for field in fields:
        row = Frame(root)
        lab = Label(row, width=15, text=field, anchor='w')
        ent = Entry(row)
        row.pack(side=TOP, fill=X, padx=5, pady=5)
        lab.pack(side=LEFT)
        ent.pack(side=RIGHT, expand=YES, fill=X)
        entries.append((field, ent))

    return entries


def changed(*args):
    """
    Tracks changes of the operator
    :param args:
    :return:
    """
    variables['curr_operator'] = s.get()


if __name__ == '__main__':
    root = Tk()

    ents = makeform(root, fields)

    s = StringVar()
    s.set('+')
    s.trace('w', changed)
    om = OptionMenu(root, s, '+', '-', '*', '/')
    om.pack()

    b1 = Button(root, text='Compute',
                command=(lambda e=ents: compute(e)))
    b1.pack(side=LEFT, padx=5, pady=5)
    b2 = Button(root, text='Quit', command=root.quit)
    b2.pack(side=LEFT, padx=5, pady=5)

    result_label = Label(root, text="")
    result_label.pack()

    root.mainloop()
\$\endgroup\$
2
  • \$\begingroup\$ Is this Python 2 or 3? From the prints it looks like Python 3, but raw_input exists only in Python 2. We have the tags python-2.x and python-3.x if your code only works in one of the two versions (in that case add them in addition to the python tag). \$\endgroup\$
    – Graipher
    Mar 2, 2019 at 14:14
  • 1
    \$\begingroup\$ Sorry I forgot to add this tag, yes I accidently wrote it in python 2.. In the next program I will use python 3 \$\endgroup\$
    – user97059
    Mar 2, 2019 at 14:40

1 Answer 1

2
\$\begingroup\$

So, your doc strings look fine. I would like to point out few things

1-) In your core class, you are returning True along with the calculated value but you don't make any validations (Except the division function) so it is always True. You could add a validation function (Maybe as a decorator) and validate the given inputs type's (For example there will be an error if you try to sum up a string and integer

2-) (from calculator_core import *) in your calculator_console script is not really a best practice since it could cause namespace collisions, you could just import it like , 'import calculator_core'

3-) In your 'find_operator_index' function you iterate and find the operator's index and after that you use it for slicing. I would rather find the operator itself and split it by the operator in that case and I would use a comprehension. Like

operator = [operator_char for operator_char in command if operator_char in SUPPORTED_OPERATORS_DICT.keys()]

And validate it like;

if len(operator) > 0:
     #there is an operator
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.