-
Notifications
You must be signed in to change notification settings - Fork 23
Aliaksei Karatynski #8
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?
Aliaksei Karatynski #8
Conversation
| import argparse | ||
|
|
||
|
|
||
| def arg_parser(): |
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.
Согласно негласным соглашениям хорошим тоном считается давать функциям названия, выражающие действие (глагол). Название parse_args подошло бы лучше.
Кроме того, объем и сложность логики данной функции очень малы для вынесения в отдельный модуль. Обычно такую функцию называют _parse_args и располагают в модуле, являющемся точкой входа приложения.
| """ | ||
| Import modules | ||
| """ | ||
| sys.path.insert(0, os.path.abspath(os.path.curdir)) |
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.
Костыль! :)
|
|
||
| def split_operands(merged_operand): | ||
| """ | ||
| Split operands |
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.
Док стринг повторяет название функции, что бессмысленно. Он может нести больше полезной информации, либо докстринг можно опустить, чтобы не захламлять код.
если есть желание уточнить принимаемый/возвращаемый типы, можно использовать тайп хинты https://docs.python.org/3/library/typing.html
| ) | ||
|
|
||
| PRIORITIES = { | ||
| 1: ['<', '>', '<=', '>=', '==', '!='], |
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.
Вместо списка для констант лучше использовать кортежи. Таким образом мы подчеркиваем, что данное перечисление не планируется изменять и используем меньше памяти.
| token_end_position = -1 | ||
| insert_positions = [] | ||
| expression = expression.replace(' ', '') | ||
| expression = re.sub(r'\)\(', ')*(', expression) |
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.
Регулярные выражения лучше выносить в отдельные переменные, чтобы программист, который читает код, смог получить дополнительную информацию о происходящем из названия переменной. Чтение названия переменной занимает намного меньше времени, чем попытка осознать смысл регулярного выражения.
| expression = list(expression) | ||
| for index in insert_positions: | ||
| expression.insert(index, '*') | ||
| return ''.join(expression) |
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 module == builtins and attribute not in BUILTINS_FUNCS: | ||
| continue | ||
| return module | ||
| return |
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 expression | ||
| :param token_end_position | ||
| :param module | ||
| :return: the constant or result of the function and |
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.
Функция должна обладать единым четким и понятным интерфейсом. Результат работы функции должен интерпретироваться единообразно.
| @@ -0,0 +1,487 @@ | |||
| """ | |||
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.
Код нуждается в декомпозиции. Например разделить модуль calculate на несколько маленьких модулей или инкапсулировать функциональность в несколько разных классах, некоторые большие функции разделить на несколько маленьких, где это возможно
| answer = do_calculation(arguments.EXPRESSION, arguments.MODULE) | ||
| print(answer) | ||
| if str(answer).startswith('ERROR'): | ||
| sys.exit(-1) |
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.
Почему -1? Не соответствует конвенции UNIX описывающей возвращаемые значения программы (от 0 до 255)
| return operator == '^' | ||
|
|
||
|
|
||
| def check_is_callable(attribute, module): |
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.
В чем смысл этой функции? Она используется в одном месте в бизнес логике приложения (исключая юнит тесты) и не содержит какую-либо существенную логику.
| @@ -1,0 +1,14 @@ | |||
| """ | |||
| This module allows to installs the application | |||
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.
Слишком очевидно чтобы быть тут.
| import sys | ||
| import operator as op | ||
| from .calculator_helper import PycalcError | ||
| from .calculator_helper import ( |
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.
В случаях, когда импортируется множество сущностей из одного и того же модуля, принято распологать их по одному на строку, для повышения читаемости.
from .calculator_helper import (
check_is_number,
check_may_unary_operator,
...
check_brackets_balance,
)| import os | ||
| import re | ||
| import sys | ||
| import operator as op |
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, что может запутать программиста, который может ожидать от этого имени использование стандартной библиотеки
| """ | ||
| arguments = args.arg_parser() | ||
|
|
||
| answer = do_calculation(arguments.EXPRESSION, arguments.MODULE) |
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.
Вместо возвращения объекта ошибки из функции do_calculation логичнее отлавливать исключения на самом высоком уровне:
try:
print(do_calculation(arguments.EXPRESSION, arguments.MODULE))
except PycalcError as e:
print(e)
except Exception as e:
print("ERROR: {}".format(e))| return True | ||
|
|
||
|
|
||
| def check_valid_spaces(expression): |
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(string) | ||
| return True | ||
| except ValueError: | ||
| return |
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.
Лучше в данном случае вернуть False
| check_valid_spaces(expression) | ||
|
|
||
|
|
||
| def update_operands(expression, operands, index): |
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.
Ни из названия функции, ни из ее описания не ясно что она делает. Кроме обновления операндов она считает некий token_end_position. Это явно указывет на ошибки, совершенные при декомпозиции (разбиении программы на отдельные независимые сущности). То же самое для функции update_operators (строка 401).
| """ | ||
| for operator in operators: | ||
| if get_priority(operator) > 1: | ||
| return |
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.
Несоответствие с докстрингом, в данном случае функция возвращает None
| condition_b = get_priority(operators[-1]) >= get_priority(operator) | ||
| condition_c = check_right_associativity(operator) | ||
| condition_d = get_priority(operators[-1]) > get_priority(operator) | ||
| condition = condition_a and condition_b or condition_c and condition_d |
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.
Плохо. Такие блоки кода очень сложно понять человеку, не знакомому с проектом. Если бы декомпозиция была проведена грамотно, в функции не было бы такого количества условий.
|
В целом в проекте прослеживается одна и та же проблема: декомпозиция (разбиение логики проекта на отдельные независимые сущности) проведена не лучшим способом. Из-за этого появились функции, выполняющие сразу несколько обязанностей. Из-за этого становится сложно подобрать функции верное и понятное название, сложно выбрать простой и четкий интерфейс. Код становится запутанным. |
No description provided.