Skip to content

Conversation

@misha-sauchuk
Copy link

now it's ready to calculate valid expressions

from argparse import ArgumentParser


parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Я очень не рекомендую писать код напрямую в глобальной секции модуля.
Вся логика должна находиться в функциях/методах. В глобальной области должны находится только определение функций, классов, констант и импорт модулей

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parser вынес в отдельную функцию. Кстати из-за того что я импортировал не модуль argparse, а ArgumentParser у меня не запускались тесты на CI.

}


class Error(Exception):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Такое исключение только усложняет понимание кода. Все, для чего оно создано, это выбрать из мапинга текст сообщения.
Использование простого исключения, в которое будет завдваться сообщение с ошибкой, будет читаться намного проще и не будет никакой подкапотной магии.

из выражения raise Error(id=4) понятно только то, что тут происходит ошибка с номером четыре. нужно искать где лежит код исключения, потом искать мапинг ошибок и только потом будет понятно, что за ошибка тут произошла

Copy link
Author

@misha-sauchuk misha-sauchuk May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Исправил, вызываю исключение SyntaxError с необходимым текстом, затем ловлю в try/except сначала SyntaxError (это пользовательские ошибки ввода), затем ошибки, которые могут возникать при вычислении (деление на ноль, логарифм отрицательного чилса)

try:
return int(number)
except:
return float(number)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а что, если и к float не получится привести?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

в функцию передается только строка с числом, т.е. не получится привести к float если число очень большое и значит надо обработать исключение "OverflowError: (34, 'Numerical result out of range')"



def function_parser(function_name):
if function_name == 'e' or function_name == 'pi':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а как же tau?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Добавил проверку версии интерпритатора для возможности определения значения tau из модуля math. Если tau отсутвует в данной версии, тогда tau = pi * 2

return converted_list


class OperandStack:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, что collections.deque замечательно подходит для этой цели.

Copy link
Author

@misha-sauchuk misha-sauchuk May 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Над этим еще работаю. Просматрел видео урок "Встроенные коллекции и модуль collections" Сергея Лебедева (и не только эту лекцию), понял что collections.deque использутся как очередь. Пока что-то не сообразил как это применить :(

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В collections.deque отсутвует метод посмотреть верхний (последний) элемент, к нему можно обратиться по индексу [-1], но мне кажется это не совсем красиво.
Я создаю объект класса Stack, что подразумевает что этот объект будет вести себя как stack, и для того что бы просто посмортеть верхний элемент у моего класса есть метод top



def calculate(converted_list):
global operands, function, func_arguments, current_operator, current_result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Использование глобальных переменных, которые планируется изменять, -- злейшее зло
Если нужно хранить состояние, то лучше использовать класс

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Создал класс, извлек его в отдельный модуль

function = OperandStack()
func_arguments = False
for item in converted_list:
if type(item) is float or type(item) is int:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isinstance(item, float)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Круто, исправил)

…rgumentParser into method arg_parse; rename file calc.py to pycalc.py
…ction without arguments, and update function_parser with check if there is such fucntion in function_dict
…tion without arguments in the end of expression; fix: raise Error if there is twice operator in the end of expression; refactor: update cheking_convertid_list to checking_parsing_list and call this function before start to convert parsing list
…efactor: update check_parsing_list - remove esle statment in first if block; refactor: update type into isinstance; feat: add OverflowError into exseptions
…efactor: update check_parsing_list - remove esle statment in first if block; refactor: update type into isinstance; feat: add OverflowError into exseptions
…tion to "while" cycle; replaced range(len(self.function.stack)) to self.function.stack in "for" cycle and "not self.function.is_empty" to "self.fucntion.stack"; style: update some docstrings in check_manager module
…sum([1,2,3,4])"; update ERROR_CASES test with wrong position of brackets (closed brackets is first)
…e: update visual indent in the test_argument_parser
@misha-sauchuk misha-sauchuk changed the title [WIP] Mikhail Sauchuk Mikhail Sauchuk Jun 5, 2019
"""Calculator class"""
def __init__(self, expression, functions):
"""
Generates an instance of the Calculator class,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Этот докстринг в точности повторяет код строчка за строчкой :)
( + __init__ не генерирует объект, а инициализирует его)

if isinstance(parsing_list[0], int) or isinstance(parsing_list[0], float):
return parsing_list
raise SyntaxError('Expression must include at list one operand!')
if parsing_list[-1] in operator_dict.keys():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

вызов метода .keys можно опустить

if parsing_list[-1] in operator_dict:
   ...

return parsing_list


def operator_check(operator_symbol):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мне кажется, что это лишняя функция и можно было легко обойтись без нее, просто проверять наличие ключа в словаре вместо вызова функции

:param operator_symbol: last_symbol (str) from instance of SplitOperators class
:return: clear operator_symbol as str
"""
if operator_symbol in operator_dict.keys():
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

и все же если оставлять эту функцию именно в таком виде, то я бы лучше ее написал вот так

if operator_symbol not in operator_dict:
    raise SyntaxError('Typo in math operator!')
return operator_symbol


def function_check(function_name, function_dict):
"""
Check if function_name is a key in function_dict.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If function_name is "pi", "e", "tau", "inf" or "nan" convert it into float
Если говорить о константах, то это точно не функции :)
скорее это проверка токена, чем функции

else:
self._append_to_converted_list(self.function_dict[function])

def converter(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Название функции должно показывать действие. По смыслу больше подходит convert

raise SyntaxError('There is no module with name {}'.format(module))


operator_dict = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Глобальные переменные обычно записывают большими буквами через нижнее подчеркивание (OPERATOR_DICT) и в начале модуля после всех импортов

calc = Calculator(expression_line.expression, expression_line.functions)
result = calc.calculate()
print(result)
except SyntaxError as err:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А если в приложении произойдет какая-нибудь другая ошибка? утилита свалиться с трэйсбэком

except SyntaxError as err:
print('ERROR: {}'.format(err))
except ZeroDivisionError as err:
print('ERROR: {}!'.format(err))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Плюс все обработчики этих исключений одинаковые

"""SplitOperators class"""
def __init__(self, expression, functions):
"""
Generates an instance of the SplitManager class,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Огромный и бесполезный докстринг

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants