Imports Style Guide
Замечательный PEP8, с которым вы наверняка знакомы, если занимаетесь разработкой на Python, не отвечает на все возникающие в процессе кодинга вопросы. В итоге каждая команда разработчиков берёт PEP8 за основу и вырабатывает собственные стандарты, которых старается придерживаться. Сколько команд – столько вариантов. И это необходимые искусственные рамки. Как сетка для типографа или верстальщика – помогает коду выглядеть чётко, выдержано и последовательно. В общем, форматирование кода – это не то место, где нужно проявлять свою индивидуальность.
Прежде чем задать основной вопрос, уважаемые коллеги, я хотел бы показать некоторые примеры «вольностей», которые каждый принимает для себя сам (или безропотно принимает какое-то чужое решение).
""" Very long multiline description about function or class method """
"""Very long multiline description about function or class method """
Расстояние между блоком импортов и первой функцией или классом – 2 пустые строки.
from unittest import TestCase class BaseURLGeneratorTestCase(TestCase): pass
Расстояние между различными функциями или классами – тоже 2 пустые строки. Расстояние между методами класса – 1 пустая строка. Расстояние между заголовком класса и первым методом – 1 пустая строка.
class TestSomething(TestCase): def test_some_stuff(self): pass def test_another_stuff(self): pass
Перечисление элементов списка/кортежа/словаря и пр. – с каждой новой строки, если всё не влазит в одну:
records = [first_object, second_object] records = [ first_object, second_object, third_object, fourth_object, ]
То же касается и длинного логического условия:
if ( user.is_client() and user.has_manager() and not user.is_banned() and user.has_company() ): do_something()
Если есть необходимость разбить цепочку методов на несколько строк, выражение обрамляется в скобки. Никаких бэкслешей (фу-фу-фу).
templates = ( EmailTemplate.query .filter(EmailTemplate.type_id == type.id) .order_by(EmailTemplate.title) .all() )
Кортеж из одного элемента записывается как x = (y,) или x = y, , но выглядит это ужасно, поэтому я предпочитаю в данном случае обходиться списком x = [y] или множеством x = , есть и другие варианты.
x = None if x: do_something() # фигня if x is not None: do_something() # так-то лучше
Кстати, комментарии отбиваются двумя пробелами, а не одним.
Помимо прочего, люблю строковый метод .format(). ».format(x) мне больше по душе, чем ‘%s’ % x . Но когда точек вставки больше двух, следует использовать именованные аргументы:
' , '.format( name=name, surname=surname, address=address, )
Implicit-соединение строк выглядит странно, но визуально приятнее конкатенации сложением:
message = _( u"Зачем соединять строки через +, " u"если есть вот такая вот фича?" )
Словарь записываем как , а не как dict(key=value, another_key=another_value) . Это визуально чище, подсвечивается всеми нормальными редакторами как словарь, а не как передача именованных аргументов, к тому же создание такого объекта отрабатывает быстрее, чем через dict().
Таких «пунктиков» наберётся не один десяток и я уверен, что найдутся люди, готовые их оспорить и предложить своё виденье кошерного оформления кода. Но если по приведённым примерам для меня всё более-менее ясно, то по оформлению больших (реально больших) блоков импортов пока не всё очевидно.
Встретил я как-то в рабочем проекте портянку импортов в одном из модулей.
. from lib.util import https_wrapper, logout_user from lib.util import ajax, sudo_su, sudo_exit, transactional from lib.util import has_auth, get_demo from lib.util import get_user_registration_source_id from lib.util import ajax_validate, generate_timed_hash, get_auth_user from lib.util import render_template, check_timed_hash from lib.util import create_validate_response, update_user_in_session from lib.util import prepare_watermark_text .
Подумал про себя: «Какого хрена. » и переписал так, как нравится мне.
from lib.util import ( https_wrapper, logout_user, ajax, sudo_su, sudo_exit, transactional, has_auth, get_demo, )
За что отгрёб во время ревью. Как бы предыдущий вариант тоже имеет право на жизнь, удобен во время мёржа и вроде как его удобней читать. Я пообещал аргументировать свой вариант и просто дам ссылку на этот пост, заодно и узнаю что об этом всём думают хабровчане.
Итак. Сама идея «каждый объект с новой строки» важна. Во-первых, это красиво 🙂 Во-вторых, во время мёржа это даёт свои преимущества, путаницы не возникает. Это полезное свойство, нам нужно его сохранить.
Во втором варианте модуль (или пакет) с которого мы импортируем – это как будто заголовок последующего абзаца текста (импортируемых объектов). Нет необходимости каждый раз повторять заголовок, мы уже увидели единожды откуда мы импортируем и у нас есть построчное перечисление чего мы импортируем. Если кому-то надо дополнить список импортов из данного источника, он просто идёт в конец списка, жмёт Enter и дописывает своё (+ запятая, с мыслью о ближнем). Выглядит чище, строки короче, считываются быстрее, не нужно каждый раз дописывать/копипастить источник, который, кстати, может переименоваться. Не нужно игнорировать взглядом часть from bla_bla_bla import для каждой строки. Проскользить глазами 4 пробела намного легче.
Вообще вариантов оформления блока с импортами масса. С десяток, не меньше. Как именно разбивать строки, по каким критериям, сортировать ли по алфавиту, переносить или нет открывающую/закрывающую скобку… Но к какому-то общему знаменателю нужно прийти, вся команда разработчиков должна придерживаться принятого соглашения. Иначе анархия. Знаю людей, которые вообще не заморачиваются на этот счёт, так как в PyCharm есть чудесный автоимпорт и есть точка зрения, что совсем неважно что творится в этом блоке. Но у нас команда большая, PyCharm использую по ходу только я (большинство на Sublime, остальные – Vim, Emacs, Eclipse), нужно позаботиться о том, чтобы всем было удобно и понятно. Ну и с моей точки зрения, импорты – тоже код, который достоен хорошо выглядеть. Maintainability, в конце концов. А что думаете вы?