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

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

НЛО прилетело и опубликовало эту надпись здесь
>разница пренебрежимо мала.
Это вы про разницу между 163 и 119? Это ведь почти 30%, особенно с учетом того, что решение-то тривиальное, и нам ничего не стоит. Я такие случаи регулярно на review ловил в реальных приложениях, и разница в конечном счете обычно была очень даже заметна.

Quick "in" check

Ничего не стоит при написании, забесплатно даёт O(1) вместо O(n), не делает лишние сравнения, если есть дубликаты в списке. Я за этот вариант.

Bulk

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

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

Concurrency safety

В коде на Python почти никогда не используются потоки.

Значит я особенный, потому что я использую постоянно. Плюс я говорил про процессы в том числе.

0.01% обязательно случится.

Asserts everywhere

items = Item.objects.get()

print(items[0])

Согласен, пример неудачный

Asserts everywhere

items = Item.objects.get()

print(items[0])

Согласен, пример неудачный


Эээ ребят, а это ничего что это не работает?
get() вернет объект у которого, скорее всего, нет [0] или упадет DoesNotExist/MultipleObjectsReturned.

Вероятно, подразумевалось Item.objects.all()[:1], но тогда это просто Item.objects.last() или Item.objects.first() в зависимости от задачи.
НЛО прилетело и опубликовало эту надпись здесь

Quick "in" check
а создание множества разве бесплатно? тот же O(n)

Пожалуйста с сравните аналогичными структурами numpy
В коде на Python почти никогда не используются потоки.

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

Спикайте уж сразу на инглише, плиз.
PS: я минуту думал, как и чем сервисы могут пораниться.
PPS: пишу ужасные слова типа «выполняться» или «работать». Эдакий «IT-суржик» оставил для молодёжи.

Эти слова намного легче говорить и понимать, ежели душные формальные обозначения, которые слышишь раз в пять лет в каком-то докладе, от которого засыпаешь через 2 минуты.


PS: я минуту думал, как и чем сервисы могут пораниться.

Боюсь представить что вы пишете людям в рабочем чате.


PPS: не успеваю за вами редактировать. Видать вас сильно задело, что людям проще использовать более удобные слова вместо непонятно чего.

Bulk
Вот это прям всем, всегда. Итак: всегда пишите код сразу для миллиона объектов, даже если у вас их сейчас два…
… На самом деле это просто, стоит только привыкнуть...


В том то и дело, что это очень НЕ просто. Часто это — небо и земля. Трудоемкость обработки миллионов и миллиардов элементов вместо десятков-сотен-тысяч может быть в тысячи раз затратнее и по деньгам, и по времени, и по ресурсам.
Понадобятся другие (обычно более сложные) алгоритмы, библиотеки и часто нетривиальные и возможно компромиссные решения.

в этом месте прервал чтение статьи и зашел в камменты поблагодарить автора за вредные советы.

Серьёзно? Он в первом примере предлагает негативную проверку ставить над позитивной. И не видит, что там вообще всё до одной строчки сокращается : return not my_value.

Но это всё фигня потому, что с первых строк он пишет, что что-то не может запомнить, что-то субъективно вооспринимает. Когда он узнает о том, сколько рекомендаций PEP существует, и что они проверяются ещё и автоматически, прогонит свой код через линтеры, получит ошибки, то тогда он крайне сильно удивится. Не знаю, перевод это статьи, или нет, но идея писать код так, как хочется, а не как рекомендуется - это чушь и опасная практика. Я после первого же примера пролистал немного его советы. Есть и неплохие: их надо выбирать - в этом проблема. Например, использование dict для замены switch/case (коих нет в пайтоне) - это разумная практика, но есть значимые исключения. Прежде-всего, стоит помнить, что elif является официальной заменой switch/case, и, вводя свой формат обработки этого оператора, человек заставляет других программистов писать не так, как рекомендуется, а как он сам хочет. Поэтому, надо знать, когда применять elif, а когда - словари.

Но автор не ссылается на best practice, PEP, popular styleguides. Он просто гонит отсебятину.

Он в первом примере предлагает негативную проверку ставить над позитивной

В целом он правильно написал, если откинуть то что там возвращается булевое значение. У нас американские инженеры уровня FAANG так пишут — позитивная проверка — простыня на 250 строк со вложенными ифами до бесконечности без какой либо декомпозиции и в конце обычный else в котором возвращается дефолтное значение чего-то там.


Когда такое видишь, понимаешь что никому чистый код не нужен, нужен только работающий бизнес с минимальным time to market, на худой конец можно написать в доке "сильно не бейте, писал давно" (так и кстати написано).

В целом, там тренарный оператор. Вполне явный.

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

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

Тернарный?

до одной строчки сокращается: return not my_value.

А как мне до одной строчки сократить
x=True
def foo(a: bool):

    if not a:
        return False

    else:
        print('foo is True')
        return True
        
print(foo(x))       

здесь print(foo(x)) напечатает foo is True

Мне сразу вспоминаются пионеры, доящие коня.

If a:

print('foo is true')

return a

return на уровне условного оператора. Бухаю на балконе, пишу со смартфона.

Ну, за пионеров!

Ты хабр со смехопанорамой перепутал.

Ну, за смехопанораму!
Чёт я тоже прибухнул. Удачи тебе!
В разделе Dangerous loops вам вообще не нужно условие, его надо было перенести в while…
Половина советов — очень спорно. Выше уже много заметили.
Я бы добавил, что:
if v == 'a':
    self.value_a = 1
elif v == 'b':
    self.value_b = 1
elif v == 'c':
    self.value_c = 1
Лучше переписать с применением словаря.
Использование setattr — крайне опасно, и его стоит по возможности всегда обходить стороной. Типы не проверяются, при рефакторинге оно не найдётся.

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

Отнюдь. В стандартной библиотеке Python они часто так используются. Тем более сейчас доступна типизированная NamedTuple, которая будет, как и аннотации типов, служить хорошей документацией.

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

У нас на проекте так сетали значения полям БД-модели алхимии с джейсона. Больше не смогу развидеть.

привет, kesn. Читал статью и кивал: вроде бы и да, а вроде бы и нет.

Писать на Python можно сразу хорошо, только у всех это свое хорошо. Тут соглашусь с David Beazley, что большинство не понимает идеологию Python вообще, но это не мешает им создавать интересные решения.

Что мне кажется полезным использовать:

1. Генераторы и Pipe-generators в любом виде.
Все, что необходимо для перечисления в последующих функциях, должно быть генератором.
Когда я вижу, что вычисляется лист/кортеж который прокидывается дальше в какой-нибудь цикл, то это означает, что алгоритм выполняет несколько ненужных проходов по коллекции элементов, прежде, чем выполнить что-то полезное. В моем теперешнем проекте можно встретить list только в одном случае: я пропустил это при рефакторинге.

2. Рефакторинг For цикла без использования Break.
Если идет цикл по набору элементов, который прерывается (break) по каким-либо причинам, оставляя часть элементов не обработанными, то получается, что набор был излишен. Ресурсы на создание и хранение этого набора частично были потрачены зря. Если же набор элементов — это генератор, не ясно, что мешало создать генератор на меньшее количество элементов?

3. Рефакторинг For цикла без использования Continue.
Если идет цикл по набору элементов, некоторые из которых пропускаются (continue) по каким-либо причинам, оставляя часть элементов не обработанными, то получается, что набор был излишен. Ресурсы на создание и хранение этого набора частично были потрачены зря. Если же набор элементов — это тоже генератор, не ясно, что мешало создать сразу правильный генератор?

4. While c break/continue является продолжением предыдущих выводов. Многие захотят поспорить, и я соглашусь, например, с тем, что использование break/continue убыстряет разработку, а это плюс!

5. map/zip в современном Python бессмысленны:
про map уже даже Гвидо сказал, что
«want to remove map»
zip стал не нужен после появления yield from:
zip = yield from (next(iterator) for iterator in tuple(iter(iterable) for iterable in iterables))

6. Ранний return — не панацея от удержания кода в голове. Если код надо запоминать — это скорее всего признак необходимости рефакторинга. А вот множественные точки выхода — это попо-боль для отладки.
Пример: поставил точку останова. А оно не остановилось… Оказалось создатель кода решил выйти из функции раньше.

7. Даже в документации Django list(queryset) отмечен, как опасный. Потому, как list запускает получение всех данных queryset.
Предлагаю Автору сделать assert list(queryset) на queryset под миллион записей и удивиться.
Кто-то посоветовал queryset.get(), но для версий django <=3.0 get имеет ту же проблему, я описывал это в своей статье.
Ложки Хорошего решения тут нет.
Иногда поможет queryset.exists(), но это n+1 запрос.
Иногда поможет выполнение кода и assert после:
item = None
for item in Item.objects.all()[:1]:  #  для твоего кода 
    print (item)
assert item  # для любителей assert-ов

8. select_related / prefetch_related ТОЛЬКО в случаях, когда это действительно надо.
Такое надо в коде бывает редко, если алгоритм выполняет то, что должен, и не более.
Пример: В Django 1.4 я прилепил select/prefetch всего сразу на базовый queryset цены товаров в магазине. Спустя 4 года любое обращение к queryset цен стало вешать базу. Я начал плакать, кода посмотрел код sql запроса price.objects.count(), который вызывается дважды в AdminModel в шаблоне change_list.html

В итоге Автор напомнил мне меня же лет 6 назад. Я тогда уже наелся первой стрельбой по ногам от python/django и сформировал кодекс «пиши вот так...». Cпустя время каждое второе правило этого кодекса оказалось очередной «случайной пулей в ногу», ведь задачи стали совершенно другими.

Автору успехов

2 и 3 - даже если выход из цикла и пропуск элементов, которые строки, взятые из файла?

AlexNoo, разумеется, каждой задаче — свой алгоритм. Не считав значения из потока/файла — не поймешь, что дальше делать: побыстрее избавляться от него или обрабатывать.

В теории, если есть brake в for-цикле при считывании из файла, то, вероятно, что лучше использовать конструкцию while.

Так же, в теории, если есть пропуск в for-цикле при считывании из файла, то, вероятно, лучше вынести пропуск элемента в блок if pipe-генератора.

На практике — решение выбирается по задаче и по тому, сколько есть времени на разработку.
Часто бывает, что влепить continue в середине for бывает выгоднее всего.

У меня в текущем legasy-проекте встречается 1653 for/while, 138 break и 39 continue. Любопытно будет спустя год найти этот комментарий и сравнить, как это соотношение изменится.
5. Почему map — это плохо?

Потому что map заменяется generator comprehension, или как оно называется:

map(fn, items) --> (fn(item) for item in items).

Работал с плюсами лет пять, потом с Пайтоном года три (и до сих пор эпизодически работаю, язык очень полюбил), потом лет восемь Си и всякой системщины.

Код-ревью не последних двух языках делал и делаю.

Часть советов для совсем новичков - здесь проблемы/решения видны сразу. Часть - весьма спорные. Сильно от задачи зависят.

Максима про "всегда пишите код сразу для миллиона объектов" не верна вообще. Помните о возможности расширения, но пишите код под задачу. А то я видел самописные квиксорты и би-деревья для данных, которые используются несколько раз в день (оценка сверху) и которые даже в отдалённой перспективе дай бог будут состоять из тысячи элементов (скорее, из ста).

Пара вещей попалась новых (я с перехода на C не следил за Пайтоном), но, скорее, "сахар". Интересный, не спорю, но на 'пишем хорошо" не тянет. С тем же успехом можно и про генераторы с лямбдами рассказывать.

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

Зачем делать ассерты, не разумнее работать сразу с датаклассами?

Где пункт "пишите комментарии"???!!!

Сейчас разбираюсь с кодом библиотеки экранов на 30 матана различного - комментариями покрыто только около 5% логики(

Комментарии в пайтоне это анти-паттерн. В классов есть class-docstrings, в методов/функций "обычные" doctrings. Вот там все должно быть расписано, а код должен быть самочитаемый и максимально декомпозированный.

Комментарии в пайтоне это анти-паттерн

Это вам такое сказал? Не водитесь с этим человеком, он вас плохому научит.

Самодокументированность хороша, когда нужно обеспечить ясность в плане того, что делается в этом коде. Но она вообще никак не помогает прояснить то, почему делается именно так.

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

Поддерживать и доробатывать такое совершенно нереально.
Это вам такое сказал? Не водитесь с этим человеком, он вас плохому научит.

Так всегда писали в Cisco. Комментарий — это code smell. Если человек оставлял в коде комментарий, то это был тревожный звоночек на code review. Поэтому код в Cisco написан так, что джуну (например когда я был ним) было абсолютно понятно все от А до Я в коде продуктов, который был написан 10 лет назад. А там повсюду чистый threading/multiprocessing/RPC, и иногда старая джанга без ORM, парсинг строк, ссылок и обработка файлов в архивах на предмет малвари. И миллион еще других крутейших вещей, которые никогда не увидять разработчики всяких API на Flask/FastAPI/DRF/Sanic. И да, там код и релизы годами делались без тестов, потому что если писать понятный код, то ошибок будет минимум, а когда один колхозит после второго и напиливает повсюду комментарии, то начинаются баги на баге, которые невозможно исправить без дичайших костылей. Я это видел своими глазами и меня не переубедить — код без тестов в дичайших процессинговых нагрузках с RabbitMQ & NFS в контексте тредов годами на продакшене без падений, и модный TDD, где сотня юзеров ломают систему.


На одном собеседовании меня спрашивали как можно писать код без тестов (даже в личных проектах), потому что все должно быть по TDD. В результате я получил оффер, пришел и понял что там с их тестами все равно ничего не работает, потому что хуяк-хуяк и в продакшен с тестами, комментариями и отсутствием декомпозиции.


Я сейчас работаю на стартап, так там все в комментариях, потому что в каждом методе по 100-250 строк с дичайшими отступами, где иф ифом погоняет и новый разработчик не рефакторит, а просто вклинивает костыли и новые комментарии. Докстрингов мало, в модулях вообще отсутствуют. Когда в Cisco за день можна разобраться как работает продукт, то в коде любителей комментариев иногда за неделю невозможно разобраться что происходить в одном API-эндпоинте.

Полезно
С assert всё неоднозначно. В django решили заменить большинство assert(не все) на генерирование исключения. В то же время в DRF решили не следовать данному примеру. Моё мнение — наличие опции "-O" у интерпретатора python как бы говорит о том что разработчики языка не планировали что assert будет использоваться для валидации.

result = get(service_url).json()
assert result in {'ok', 'fine', 'good'}

это прям идеальный пример неправильного использования assert — внешние данные им проверять точно не стоит.

У нас внутри компании тоже был холивар на тему ассертов. Начиная с фланга -О - если он отменяет ассерты, то зачем они вообще нужны? Ключевое слово только для тестов? Ну такое себе. Плюс кто те люди, кто юзает -О?

Ну и exception (в частности ValueError) vs assert - где что использовать? Я написал, как считаю верным

def foo(a: bool):

    if a:
        #
        # ... 50 LOCs
        #
        return True
        
    else:  # if not a
        return False

Бальзам на раны. Как только я, впервые, встретил эту конструкцию:

 if A = B then

    C = D;

  else

    C = E;

в 1976 году в языке PL/I, то сразу понял, что это засада )

Старался всегда делать как Вы предлагаете.

Более того, сейчас 8 лет работаю в команде над проектом и за первые 3 года отучил всех искользовать ELSE.

Признаюсь, что это было не просто, но я визировал все пул реквесты и стоял на своем.

А остальные 5 лет отучаете всех использовать IF, верно?

С самого начала, при возможности, просил вместо IF использовать SWITCH / CASE конструкции и DEFAULT, обязательно, если CASE не угадали )
Да там, вообще, много мест для борьбы на чистый код было. Унифицирование названий функций, переменных, констант.
Поля в базе требовал с префиксами и заглавными буквами писать. С литералами типа == 'Yes' очень тяжело бороться. Вообще запрещал любые константы подобного типа.
За семь лет проект подрастал. Приходили новые codemonkey (не знаю русского слова про таких программистов) со своим подчерком и надо было их поправлять

К слову, над чем я работаю в свободное время и там else не использую. Вот в моем коде
https://colab.research.google.com/drive/1X_rf3nZE9kQMSPTeXkHGK-rOyLXlBLtS?usp=sharing
(!!! файл в работе. Это черновик)
в VAOP tutorial в функции
def getDirectionCodeDependedOfTheValueOfTheCurrentElementOfArray(va_data):
хорошо видно как я без else пишу.
Надо помнить что в реальных задачах с многими directions эта функция будет очень сложная и её надо, явно, без else писать
Вот здесь более сложный пример по ходу лекции: (!!! файл в работе. Это черновик)

https://colab.research.google.com/drive/1_Bam5obI29R0GUWG7Pb6zUY-FeQ_KkTp?usp=sharing

При обычном подходе (без VAOP) очень разные коды у всех получаются. Я на фрилансе нанимал программеров (алжирцев, русских, египтян, украницев, немцев ...) и просил решить эту простенькую задачу. Потом в статье покажу какой это кошмар выходит. Особенно когда просишь сделать очередной апдейт (from Update-20 to Update-30 for example) на основе чужого кода. Хотя, в жизни это почти всегда так, но они уже просили втрое больше денег, а чаще, вообще отказывались )

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

Публикации