-
Notifications
You must be signed in to change notification settings - Fork 27
Mikhail Sauchuk #15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Mikhail Sauchuk #15
Conversation
…xpressions with more than one argument.
add list of examples of the expressions
…and define the main function to run the calculator
…n of the users experession, add try-exsept block to main function
final_task/calc.py
Outdated
| from argparse import ArgumentParser | ||
|
|
||
|
|
||
| parser = ArgumentParser(description='Pure-python command-line calculator.', prog='pycalc') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я очень не рекомендую писать код напрямую в глобальной секции модуля.
Вся логика должна находиться в функциях/методах. В глобальной области должны находится только определение функций, классов, констант и импорт модулей
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parser вынес в отдельную функцию. Кстати из-за того что я импортировал не модуль argparse, а ArgumentParser у меня не запускались тесты на CI.
final_task/calc.py
Outdated
| } | ||
|
|
||
|
|
||
| class Error(Exception): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Такое исключение только усложняет понимание кода. Все, для чего оно создано, это выбрать из мапинга текст сообщения.
Использование простого исключения, в которое будет завдваться сообщение с ошибкой, будет читаться намного проще и не будет никакой подкапотной магии.
из выражения raise Error(id=4) понятно только то, что тут происходит ошибка с номером четыре. нужно искать где лежит код исключения, потом искать мапинг ошибок и только потом будет понятно, что за ошибка тут произошла
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Исправил, вызываю исключение SyntaxError с необходимым текстом, затем ловлю в try/except сначала SyntaxError (это пользовательские ошибки ввода), затем ошибки, которые могут возникать при вычислении (деление на ноль, логарифм отрицательного чилса)
final_task/calc.py
Outdated
| try: | ||
| return int(number) | ||
| except: | ||
| return float(number) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а что, если и к float не получится привести?
There was a problem hiding this comment.
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')"
final_task/calc.py
Outdated
|
|
||
|
|
||
| def function_parser(function_name): | ||
| if function_name == 'e' or function_name == 'pi': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
а как же tau?
There was a problem hiding this comment.
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
final_task/calc.py
Outdated
| return converted_list | ||
|
|
||
|
|
||
| class OperandStack: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мне кажется, что collections.deque замечательно подходит для этой цели.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Над этим еще работаю. Просматрел видео урок "Встроенные коллекции и модуль collections" Сергея Лебедева (и не только эту лекцию), понял что collections.deque использутся как очередь. Пока что-то не сообразил как это применить :(
There was a problem hiding this comment.
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
final_task/calc.py
Outdated
|
|
||
|
|
||
| def calculate(converted_list): | ||
| global operands, function, func_arguments, current_operator, current_result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Использование глобальных переменных, которые планируется изменять, -- злейшее зло
Если нужно хранить состояние, то лучше использовать класс
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Создал класс, извлек его в отдельный модуль
final_task/calc.py
Outdated
| function = OperandStack() | ||
| func_arguments = False | ||
| for item in converted_list: | ||
| if type(item) is float or type(item) is int: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isinstance(item, float)
There was a problem hiding this comment.
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
… with import method
| """Calculator class""" | ||
| def __init__(self, expression, functions): | ||
| """ | ||
| Generates an instance of the Calculator class, |
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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(): |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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 = { |
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
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)) |
There was a problem hiding this comment.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Огромный и бесполезный докстринг
now it's ready to calculate valid expressions