Как стать автором
Обновить

Комментарии 49

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

Не магические приватные методы располагаются ниже магических и публичных

Магические и приватные это как теплое и мягкое. Лучше "Не магические приватные методы" заменить на "Приватные методы"

Может быть "правила" заменить на "рекомендации", по аналогии с PEP8: делайте так, если у вас нет веской причины делать по другому.

Аннотации это хорошо, и в основном просто. В редких случаях приходиться постараться, чтобы они работали и проходили линтеры.

Докстетсты это очень круто. Но в проекте где зависимости генерировались в рантайме, я не смог заставить их работать.

Магические и приватные это как теплое и мягкое. Лучше "Не магические приватные методы" заменить на "Приватные методы"

Согласен, заменил, спасибо.

Может быть "правила" заменить на "рекомендации", по аналогии с PEP8

Дело в том, что для меня это именно правила, и я всегда им следую, поэтому так и назвал. Возможно, кто-то тоже захочет какие-то из них сделать своими "правилами".

На счет доктестов, я пока не смог настроить их в Django-проекте, запускающемся через Docker. Точнее не "не смог", а не нашел времени чтобы разобраться.

Правило №6 - слишком длинное имя (cooking_time_in_minutes) переменной получается. Я конечно понимаю, что сейчас все IDE умные и поддерживают автозавершение кода, но все таки... Майкл Доусон еще писал, что переменные должны быть лаконичными, то есть понятными и короткими, а не быть размером с небольшое предложение.

Правило №7 - интересно, сам не догадался, возьму на вооружение.

Правило №6 - слишком длинное имя

Согласен что длинное, но не согласен что слишком. 23 символа по мне не так много, можно спокойно писать, не нарушая PEP. Из авторов я больше следую советам Роберта Мартина, а он, если не ошибаюсь, писал что не столь важна длина, сколько важная ясность названия

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

Логика в том, что длинные имена переменных без IDE не удобно писать и повышается риск опечатки, если ты торопишься и есть похожие имена. Сидишь после работы и дописываешь на голом энтузиазме программу, и так устал и бдительность усыплена, а тут еще и длинные похожие имена переменных, IDE предложила не то, а ты не до конца прочитал что она предложила. Имена ведь вполне могут быть похожие из-за некоторой схожести по назначению.

Опять же, это личное дело программиста. Красиво, понятно и лаконично именовать можно и с тем, и с другим подходом.

длинные имена переменных без IDE не удобно писать

Мне кажется сейчас абсолютно все пишут с помощью IDE.

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

IDE предложила не то, а ты не до конца прочитал что она предложила

Кстати в такой ситуации может помочь TDD.

Разработка через тестирование, ну да. Однако, я хотел сказать другое и по сути, мою мысль выразил KizhiFox, достаточно убрать одно _ и in и уже вполне понятно и не длинно.

Лично я бы сократил до cooking_time_mins (не min, иначе неоднозначно с minimal) и flight_time_sec. Всё ещё прекрасно понятно, но при этом в два раза короче.

Тоже вариант

from typing import NewType

# более строгий
Minutes = NewType('T', int)
# менее строгий
Minutes = int

cooking_time: Minutes = 60

Есть вариант использования type hints с типом или алиасом вместо суффикса переменной

Вариант интересный, но тогда при чтении кода не будет видно что cooking_time измеряется в минутах. Придется наводиться на эту переменную, и только тогда мы увидим тип Minutes. Т.е. не получится нормального беглого чтения

Так я и не могу принять в своё сердце type hint в питоне. Всё прекрасно работало в докстрингах, с описанием типов аргументов, мета информации и возвращаемого значения. Оно выглядело эстетически лучше, парсилось любым IDE и могло быть автоматически свёрнуто, чтобы не отъедать место.

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

А можете привести пример с описанием в докстрингах? Просто не уверен что правильно вас понял

def some_function_with_lots_of_args(
    name,
    position,
    some_list = None,
    bad_desision = {}
):
    """
    This function used for testing purposes

    Args:
        name (str): the name of some poor soul
        position (common.common_enums.PositionEnum): the target position of the 
            person
        some_list ([common.departments] | None): assosiated departments
        bad_desision (dict): some additional meta, use with cautious

    Returns:
        string: Identificator for the person

    Raises:
        Attribute error
        common.errors.SomeCustomError: i got tired of making uo descriptions
    """
    

Например, пример на гуглостайловых докстрингов (подобного вида использовали в работе). Мог где то ошибиться, потому что давно не писал на питоне.

Не скажу за все IDE, но тот же pycharm спокойно умел парсить эти докстринги и выполнять статический анализ на их основе. При этом можно было обойтись без импортов всего из common в файле, если оно явно не используется.

Понял, спасибо. Да, выглядит интересно и понятно. Наверно, писать докстрингами или хинтами - это дело вкуса. Я вот такие докстринги никогда не писал и как-то привык к хинтам. Имхо с ними код выглядит короче, а вместо описания аргумента, можно сделать ему понятное название.

Мне кажется, что истина в разумном комбинировании этих способов. По большей части, подробное описание позволяет подготовиться к тому, что ты увидишь дальше, но в условном getter методе, как мне кажется, подобное может быть избыточным.

Да, нужно находить золотую середину. Например тот же type hint нет смысла указывать для __init__, ибо все и так знают для чего этот метод и что он возвращает

Конечно, к любым рекомендациям надо подходить разумно и не писать доки ради доков

Идеально:

# Код файла constants.py
MIN_IN_SECONDS = 60
HOUR_IN_SECONDS = MIN_IN_SECONDS * 60
# Код файла script.py
from constants import HOUR_IN_SECONDS
flight_time_in_seconds = HOUR_IN_SECONDS * 3

В питоне нехватает стандартизации таких вещей. Нравится, как сделано в расте:

use chrono::Duration;

let flight_time: Duration = chrono::hours(3);

Обратите внимание, что из названия переменной пропала часть "_in_seconds", потому что это не int. То есть имеется универсальная Duration, которую можно создавать из секунд, часов и чего угодно в обе стороны, и которую поддерживают все.

Да, выглядит удобно. В Python, насколько я знаю, такого нет. Можно конечно создавать свой такой класс, но каждый раз это делать - не особо хочется

Ну в это конкретном случае, я б порекомендовал datetime.timedelta(hours=3). А вот к MIN_IN_SECONDS & HOUR_IN_SECONDS у меня были б большие вопросы на ревью

А что не так с этими константами?

Если предположить, что по каким-то причинам нельзя использовать timedelta? Ну это скорей на уровне ощущения.

Одно из главных назначений констант - придать смысла бессмысленным числам (мы же тут про числовые константы). Т.е. такой код я поддерживаю

# Developer's performance is measured in lines of code
LOC_PER_HOUR = 100
WORKING_HOURS_PER_DAY = 8
LOC_PER_DAY = LOC_PER_HOUR * WORKING_HOURS_PER_DAY
WORKING_DAYS_PER_WEEK = 5
LOC_PER_WEEK = LOC_PER_DAY * WORKING_DAYS_PER_WEEK

Но в случае если переменная называется flight_time_in_seconds, то 60 там не бессмысленно. Можно сказать, что это уже мета-константа в мозгу, и на ревью я б предложил остановиться на 60 * 60 * 3

Понял о чем вы, спасибо за хороший пример. И соглашусь с вами, но все же если брать пример из статьи, то лично я быстрее пойму такую запись: HOUR_IN_SECONDS * 3, чем: 60 * 60 * 3

И быстрее пойму куда идти чтобы поменять это число. Но это имхо

Представьте, что мы не пишем код, а удаляем flight_time_in_seconds. Где гарантии, что HOUR_IN_SECONDS не останется висеть мертвым, уже никому не нужным кодом? Как понять, что на HOUR_IN_SECONDS ни кто больше не завязан и его удаление будет безопасным? Значит нужно заблаговременно позаботиться и убрать лишнее из all (а ещё лучше в приватный модуль). Вау, архитектура.

С другой стороны, всех этих проблем можно легко избежать, если не плодить сущности сверх меры. Если не подходит timedelta (допустим, нам эту константу нужно сериализовывать), и нужно лишний раз напомнить читателям кода про нюансы арифметики, я бы рекомендовал ограничиться комментарием как в "Лучше, но всё ещё плохо". К тому же, IDE потом будет выводить этот коммент в подсказке.

В целом согласен. В данном примере еще и KISS нарушается.

К тому же, IDE потом будет выводить этот коммент в подсказке.

Какая именно IDE? VS Code например не выводит комменты как подсказку

Я напутал. Нужен докстринг, и они в питоне пишутся снизу:

x = 42
"""42 is the answer"""

x  # докстринг будет в подсказке

но смысл тот же :)

Ого, впервые такую запись вижу. Спасибо

  1. __new__ (если такой метод используется в классе);

  2. __init__;

  3. Остальные магические методы;

  4. Public-методы;

  5. Protected-методы;

  6. Private-методы.

Protected в Python? Первый раз услышал, пошёл в гугл, оказывается методы с одним нижним подчёркиванием считаются protected, но по факту они ни разу не protected, в чём смысл тогда? Обычно сколько встречаю, считается нормой как раз приватные объявлять с одним нижним подчёркиванием, что является просто согласием в python сообществае, т.к. два нижних всё равно по факту приватности не дают, а тут вдруг protected...

2 нижних - метод нельзя вызывать у объекта напрямую. Хотя есть обходной путь, но его обычно не используют. В Python инкапусляция работает на уровне соглашений и если у метода 2 нижних подчеркивания, то к нему обращаемся только внутри класса.

1 нижнее - можно вызывать напрямую, но лучше этого не делать. Обычно используется при наследовании, когда хочешь сделать метод приватным для всего, кроме родственных классов. Я часто использую при работе с миксинами. Опять же, работает на уровне соглашений.

Да, как таковой приватности нет, но так работает Python, ему она и не нужна. Кстати есть одна библиотека с декораторами private и public, которые являются подобием private и public в других языках, например C++. Но, к сожалению, не помню ее названия

Protected на уровне соглашения - в любой произвольной версии интерфейс может поменяться и ты можешь использовать только на свой страх и риск.

Если такое произойдет, то скорее всего в мажорной версии, что не так страшно.

В документации эти типы называются `non-public part of the API` (одиночное подчёркивания, применяется для функций, классов, аттрибутов классов, переменных) и `class-private members` (двойное подчёркивание, только аттрибуты класса).

https://docs.python.org/3/tutorial/classes.html#private-variables

Оба случая поддерживаются на уровне языка.

"non-public part of the API" не импортируются при импортах звёздочкой.
А приватные методы класса не оверрайдятся при наследовании.

 (двойное подчёркивание, только аттрибуты класса).

А как же методы?

Двойное подчеркивание не помешает вам использовать метод, просто усложнит доступ, заставляя использовать _<ClassName>__<fieldName> (это называется name mangling), намекая, что что-то пошло не так.

MIN_IN_SECONDS - неудачное, по-моему, название для переменной. Транслит с русского на английский, как тут "минута в секунд(ах/ы?)", в целом кажется неудачным подходом к именованию. Напоминает первые лабораторные студентов по программированию. Лучше SECONDS_PER_MINUTE или MINUTE_TO_SECONDS.

В первых лаборатнорных переменные обычно именуются как-то так: a, b, c, var, list, value.

MIN_IN_SECONDS - минута в секундах. Также как вес в килограммах WEIGHT_IN_KILOGRAMS

НЛО прилетело и опубликовало эту надпись здесь

Согласен. Но как и "Тонна в килограммах", так и "Минута в секундах" звучат на мой взгляд понятно. Т.е. "сколько будет одна тонна в килограммах" и "сколько будет одна минута в секундах"

Плохо:

cat_name: str = "Tom"

Не согласен с предложением избегать type hint, «если тип переменной и так понятен».

Прямо здесь, прямо сейчас type hint выглядит, действительно, несколько наивно, но когда сотней строк ниже появится желание засунуть в cat_name что-то другое (bytes, например), у IDE будет возможность побурчать.

Как-то до этого не додумался. Спасибо.

у IDE будет возможность побурчать.

И сам программист, пожалуй, поймет что делает что-то не то)

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

Не используйте это правило если пишите на Django, и в вашем коде есть функция gettext. Её принято заменять на нижнее подчёркивание.

Не используйте нижнее подчеркивание как функцию, поскольку ее принято использовать для неиспользуемых элементов (практически во всех языках).

Для функции перевода используйте сокращение t

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

Хорошо:

# settings.PAGE_SIZE может иметь значение разных типов, например str и int page_size: int = settings.PAGE_SIZE

А что же тут хорошего, если мы используем комментарий там, где можно использовать тайпхинты?

# python <3.10
from typing import Union
page_size: Union[int, str] = settings.PAGE_SIZE
# python >=3.10
page_size: int | str = settings.PAGE_SIZE

# settings.PAGE_SIZE может иметь значение разных типов, например str и int 

Это комментарий для читающих статью, а не для читающих код. Просто я поместил его в код, чтобы было удобнее. В реальности его бы не было.

page_size: int = settings.PAGE_SIZE

С помощью int мы показываем что ожидаем именно этот тип, хотя если убрать type hint, то можно ждать int, str или что-то ещё

Проблема понятных и достаточно длинных имён переменных у меня в том, что одна простая формула или f-строка становится длинной и pylint или flake8 начинают ругаться на длину строки. Как с этим борятся? Какую длину строки для линтера ставить считается правильным?

Зависит от выбранных правил при написании кода. Если вы пишите один, то сами выбираете длину строк (я следую PEP8, поэтому для меня это 79). Если пишите в команде, то тимлид или руководитель должен выбирать правила, которые будет соблюдать вся команда. В том числе и длину строк.

На счет pylint и flake, если не ошибаюсь, можно настроить макимальную длину. Либо на крайний случай сделать --disable=<код_ошибки> и ошибка не будет появляться.

Обычно такие тонкости как длина строк - дело вкуса, поэтому можно встретить как 79, так и 120 символов на одной строке.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории