Skip to content

Conversation

@astronomer-alkor
Copy link

No description provided.

@astronomer-alkor astronomer-alkor changed the title [WIP] Karatynski Aliaksei [WIP] Aliaksei Karatynski Nov 19, 2018
@astronomer-alkor astronomer-alkor changed the title [WIP] Aliaksei Karatynski Aliaksei Karatynski Dec 16, 2018
import argparse


def arg_parser():

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))
Copy link
Owner

@PythonAndGoCommunity PythonAndGoCommunity Jan 30, 2019

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

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: ['<', '>', '<=', '>=', '==', '!='],

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)

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)

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

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

Choose a reason for hiding this comment

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

Функция должна обладать единым четким и понятным интерфейсом. Результат работы функции должен интерпретироваться единообразно.

@@ -0,0 +1,487 @@
"""

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)

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):

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

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 (

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
Copy link
Owner

@PythonAndGoCommunity PythonAndGoCommunity Jan 31, 2019

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)

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):

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

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):
Copy link
Owner

@PythonAndGoCommunity PythonAndGoCommunity Jan 31, 2019

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

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

Choose a reason for hiding this comment

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

Плохо. Такие блоки кода очень сложно понять человеку, не знакомому с проектом. Если бы декомпозиция была проведена грамотно, в функции не было бы такого количества условий.

@PythonAndGoCommunity
Copy link
Owner

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

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