Pull to refresh

Советы Google по кодированию на языке Python. Часть вторая: советы по форматированию исходного кода

Reading time14 min
Views81K

Доброго времени суток. Вот и пришло время для публикации второй части так понравившегося многим хабровчанам перевода стайл гайда для языка Python от компании Google, (первая часть бережно хранится хабром). Теперь мы коснемся напрямую форматирования исходного кода на языке программирования Python. Как известно, чистота — залог здоровья, а чистота программного кода — залог уважения коллег и (в идеале) поощрения от кого-нибудь свыше. Вообще, Python сам по себе является хорошо читаемым языком, и даже синтаксис данного языка призывает к порядку в коде (и, как следствие — в голове). Но каждый из нас сам себе документатор и сам себе творец оформления. А как уже говорилось однажды — ко мнению авторитетных товарищей нельзя не прислушиваться. Итак, вторая часть Google Python Style Guide — Python Style Rules ждет Вас под катом. И pdf тут как тут.

Python Style Rules


Точки с запятой

Не разделяйте ваши строки с помощью точек с запятой
Не разделяйте ваши строки с помощью точек с запятой и не используйте точки с запятой для разделения команд, находящихся на одной строчке.

Длина строки

Максимальная длина строки - 80 символов
Исключения

  • Длинные строки импорта
  • URL в комментариях

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

Хорошо:

foo_bar(self, width, height, color='black', design=None, x='foo',
        emphasis=None, highlight=0)

if (width == 0 and height == 0 and
    color == 'red' and emphasis == 'strong'):


Когда Ваш текст не помещается в одну строку, используйте скобки для явного объединения строк.

x = ('This will build a very long long '
     'long long long long long long string')

Что касается комментариев, располагайте длинный URL, если это необходимо, на одной строке.

Хорошо:

 # See details at
 # http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html


Плохо:

 # See details at
 # http://www.example.com/us/developer/documentation/api/content/\
 # v2.0/csv_file_name_extension_full_specification.html

Обратите внимание на отступ элемента в строке выше (смотрите раздел про отступы, чтобы получить разъяснения).

Скобки

Используйте скобки экономно
Не используйте их (скобки) с выражением return или с условной конструкцией, если не требуется организовать перенос строки. (Смотрите выше). Однако скобки хорошо использовать для создания кортежей.

Хорошо:

if foo:
    bar()
while x:
    x = bar()
if x and y:
    bar()
if not x:
    bar()
return foo
for (x, y) in dict.items(): ...


Плохо:

if (x):
    bar()
if not(x):
    bar()
return (foo)


Отступы

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

Хорошо:

# Aligned with opening delimiter
foo = long_function_name(var_one, var_two,
						 var_three, var_four)

# 4-space hanging indent; nothing on first line
foo = long_function_name(
				var_one, var_two, var_three,
				var_four)


Плохо:

# Stuff on first line forbidden
foo = long_function_name(var_one, var_two,
     var_three, var_four)

# 2-space hanging indent forbidden
foo = long_function_name(
  var_one, var_two, var_three,
  var_four)


Пустые строки

Должно быть две пустые строки между объявлениями верхнего уровня
Должно быть две пустые строки между объявлениями верхнего уровня, одна пустая строка должна быть между объявлениями методов. Две пустые строки должны быть между объявлениями верхнего уровня, будь это класс или функция. Одна пустая строка должна быть между определениями методов и между объявлением класса и его первым методом. Используйте одиночную пустую строку как вам заблагорассудится внутри функций и методов.

Пробелы

Следуйте стандартным типографским правилам, касающимся использования пробела в пунктуации
Никаких пробелов внутри каких-либо скобок.
Хорошо: spam(ham[1], {eggs: 2}, [])
Плохо: spam( ham[ 1 ], { eggs: 2 }, [ ] )

Никаких пробелов перед запятой, точкой с запятой, либо точкой. Используйте пробел после запятой, точки с запятой или точкой, исключая тот случай, когда они находятся в конце строки.

Хорошо:

if x == 4:
     print x, y
x, y = y, x

Плохо:

if x == 4 :
    print x , y
x , y = y , x

Никаких пробелов перед открывающей скобкой, которая начинает список аргументов, индекс или срез.
Хорошо: spam(1)
Плохо: spam (1)
Хорошо: dict['key'] = list[index]
Плохо: dict ['key'] = list [index]
Окружайте бинарные операторы одиночными пробелами с каждой стороны, это касается присваивания (=), операторов сравнения (==, <, >, !=, <>, <=, >=, in, not in, is, is not), и булевых операторов (and, or, not). Используйте как вам покажется правильно окружение
пробелами по отношению к арифметическим операторам, но всегда расстановка пробелов по обоим сторонам бинарного оператора будет придавать целостность Вашему коду.
Хорошо: x == 1
Плохо: x<1
Не используйте пробелы по сторонам знака "=", когда вы используете его чтобы указать на именованный аргумент или значение по-умолчанию.

Хорошо:

def complex(real, imag=0.0): return magic(r=real, i=imag)

Плохо:

def complex(real, imag = 0.0): return magic(r = real, i = imag)

Не используйте пробелы для вертикального выравнивания кусков последовательных строк, так как такие выравнивания оказываются обременительными. (Относится и к :,#,=, и т.д.):

Хорошо:

foo = 1000  # comment
long_name = 2  # comment that should not be aligned

dictionary = {
      'foo': 1,
      'long_name': 2,
      }

Плохо:

  foo       = 1000  # comment
  long_name = 2     # comment that should not be aligned

  dictionary = {
      'foo'      : 1,
      'long_name': 2,
      }


Строка #! (хэш-бэнг)

Большинство файлов .py не нуждаются в запуске через строку #!
Запускайте главный файл программы с помощью #!/usr/bin/python. Эта строка используется ядром, чтобы найти интерпретатор Python, но игнорируется Python-ом, когда импортируются модули. Это необходимо для файла, который выполняется напрямую.

Комментарии

Будьте уверены в использовании правильного стиля
Будьте уверены в использовании правильного стиля для модуля, функции, метода или строкового комментария.

Строки документации.

Python имеет уникальный стиль комментирования — строки документации. Строка документации это строка, которая является первой конструкцией в пакете, модуле, классе или функции. Такие строки могут быть экспортированы автоматически с помощью атрибута объекта __doc__ и используются pydoc-ом. (Попробуйте запустить pydoc на своем модуле, чтобы увидеть как это выглядит.) Наше соглашение по строкам документации велит использовать три двойные кавычки для обрамления такой строки. Строки документации должны быть организованы как суммарная строка (одна физическая строка), сносящаяся по кол-ву символов, знаку вопроса или восклицательному знаку, следующим за пустой строкой, а затем остальные строки документации с позиции курсора в качестве первой кавычки первой строки. Ниже описано еще больше информации по оформлению строк документирования.

Модули

Каждый файл должен содержать в себе шаблон лицензии. Выберите подходящий шаблон лицензии для вашего проекта.(Например, Apache 2.0, BSD, LGPL, GPL).

Функции и методы

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

Строка документирования должна давать достаточно информации, чтобы оформить вызов функции без чтения ее исходного кода. Строка документирования должна описывать синтаксис вызова функции и ее семантику, но не должна описывать ее реализацию. Для хитрого кода комментарии внутри исходного кода более предпочтительны, чем строки документации. Определенные аспекты функции должны быть задокументированы в специальных секциях, перечисленных ниже. Каждая секция начинается со строки заголовка, которая заканчивается точкой. Секции должны иметь отступ в два пробела, за исключением заголовочной.
Args:
Перечислите каждый параметр по имени. Описание должно следовать сразу за именем и быть разделено точкой и пробелом. Если описание слишком длинное, чтобы уместить его в в 80 символов, используйте подвешенный отступ в 2 или 4 пробела (будьте последовательны в оформлении всего файла).
Описание должно ссылаться на требуемый тип(ы) и назначение аргумента. Если функция позволяет *foo (списки аргументов переменной длины) и/или **bar (произвольный набор аргументов типа ключ-значение), они должны быть записаны как *foo и **bar.
Returns: (либо Yields для генераторов)
Опишите тип и семантику возвращаемого значения. Если функция всегда возвращает None, то данный раздел необязателен.
Raises:
Список всех исключений, которые возможны для данного интерфейса.

def fetch_bigtable_rows(big_table, keys, other_silly_variable=None):
   """Fetches rows from a Bigtable.

   Retrieves rows pertaining to the given keys from the Table instance
   represented by big_table.  Silly things may happen if
   other_silly_variable is not None.

   Args:
       big_table: An open Bigtable Table instance.
       keys: A sequence of strings representing the key of each table row
          to fetch.
       other_silly_variable: Another optional variable, that has a much
          longer name than the other args, and which does nothing.

   Returns:
       A dict mapping keys to the corresponding table row data
       fetched. Each row is represented as a tuple of strings. For
       example:

       {'Serak': ('Rigel VII', 'Preparer'),
       'Zim': ('Irk', 'Invader'),
       'Lrrr': ('Omicron Persei 8', 'Emperor')}

       If a key from the keys argument is missing from the dictionary,
       then that row was not found in the table.

   Raises:
       IOError: An error occurred accessing the bigtable.Table object.
   """
   pass


Классы

Классы должны иметь строку документации ниже своего объявления. Если Ваш класс имеет публичные атрибуты, они должны быть документированы тут же в разделе Attributes и следовать тому же стилю форматирования, что и раздел Args.

class SampleClass(object):
   """Summary of class here.

   Longer class information....
   Longer class information....

   Attributes:
       likes_spam: A boolean indicating if we like SPAM or not.
       eggs: An integer count of the eggs we have laid.
   """

   def __init__(self, likes_spam=False):
       """Inits SampleClass with blah."""
       self.likes_spam = likes_spam
       self.eggs = 0

   def public_method(self):
       """Performs operation blah."""


Блоки и инлайновые комментарии


Последнее место, которое должны иметь комментарии — это хитрые места в коде. Если Вы хотите пояснить их в Вашем следующем код-ревью, то вы должны прокомментировать их сейчас. Сложные операции, занимающие несколько строк документации перед ее выполнением. Неявные части должны иметь комментарий в конце строки.

# We use a weighted dictionary search to find out where i is in
# the array.  We extrapolate position based on the largest num
# in the array and the array size and then do binary search to
# get the exact number.

if i & (i-1) == 0:        # true iff i is a power of 2


Чтобы улучшить читаемость, такие комменарии должны находиться на расстоянии по меньшей мере 2-х пробелов от кода. С другой стороны, лучше вообще не описывайте код. Преположите, что человек, читающий данный код, знает Python (а не то, что вы пытались делать) лучше, чем Вы.
# BAD COMMENT: Now go through the b array and make sure whenever i occurs
# the next element is i+1


Классы

Если класс не наследуется от каких либо классов, явно наследуйтесь от класса object
Это также касается вложенных классов.

Хорошо:

class SampleClass(object):
     pass

class OuterClass(object):

        class InnerClass(object):
                pass


class ChildClass(ParentClass):
     """Explicitly inherits from another class already."""


Плохо:

class SampleClass:
       pass

    class OuterClass:

       class InnerClass:
           pass


Наследование от класса object необходимо, чтобы позволить свойствам работать правильно, и защитит Ваш код от возможной несовместимости с Python 3000. В нем так же определяются специальные методы, которые реализуют стандартную семантику объекта, например: __new__, __init__, __delattr__, __getattribute__, __setattr__, __hash__, __repr__, и __str__.

Строки

Используйте оператор % для форматирования строк
Используйте оператор % для форматирования строк, даже если все параметры являются строками. Тщательно взвесте использование оператора + взамен оператора %.

Хорошо:

x = a + b
x = '%s, %s!' % (imperative, expletive)
x = 'name: %s; score: %d' % (name, n)


Плохо:

x = '%s%s' % (a, b)  # use + in this case
x = imperative + ', ' + expletive + '!'
x = 'name: ' + name + '; score: ' + str(n)

Избегайте использования операторов + и +=, чтобы сконкатенировать строку при помощи цикла, т.к. строки — это неизменяемый тип данных, такой подход создает ненужные объекты и увеличивает время работы по квадратичному, а не линейному закону. Вместо этого просто добавьте каждую подстроку в список и используйте метод join после того, как цикл завершится ( или записывайте каждую подстроку в буфер cStringIO.StringIO)

Хорошо:

items = ['<table>']
for last_name, first_name in employee_list:
     items.append('<tr><td>%s, %s</td></tr>' % (last_name, first_name))
items.append('</table>')
employee_table = ''.join(items)

Плохо:

employee_table = '<table>'
for last_name, first_name in employee_list:
       employee_table += '<tr><td>%s, %s</td></tr>' % (last_name, first_name)
employee_table += '</table>'

Используйте “”” для многострочных строк вместо “”. Заметьте, однако, что всегда лучше использовать явное объединение строк, т.к. многострочные строки не продолжаются до конца программы с таким же отступом.

Хорошо:

print ("This is much nicer.\n"
      "Do it this way.\n")

Плохо:

print """This is pretty ugly.
Don't do this.
"""


Файлы и сокеты

Явно закрывайте файлы и сокеты, когда Вы заканчиваете работу с ними
Пренебрежение открытыми файлами, сокетами и другими файлообразными объектами имеет много побочных эффектов, таких как:
  • Они могут потреблять ограниченные системные ресурсы, такие как файловые дескрипторы. Код, который связан со множеством объектов, может приводить к истощению тех ресурсов без пользы, если они не будут возвращены системе после использования.
  • Содержание файлов в открытом виде может препятствовать другим действиям быть произведенными над ними, таким как, например, перемещение или удаление.
  • Файлы и сокеты, которые общедоступны через код, могут быть ненароком прочитаны или записаны после того, как они были логически «закрыты».

Если они реально закрыты, попытка чтения или записи вызовет исключение, тем самым выявляя проблему очень быстро. Более того, файлы и сокеты автоматически закрываются, когда вызывается декструктор объекта, но связывать время жизни объекта с состоянием файла — это плохая практика по следующим причинам:
  • Нет гарантий, что во время выполнения программы будет вызван декструкор объекта. Разные реализации Python используют разные подходы к управлению памятью, такие как отложенная сборка мусора, которая может существенно продлить жизнь «удаленного» объекта.
  • Неожиданные ссылки на файл могут заставить его «жить» дольше, чем мы этого ожидаем (например, стек исключений, глобальные объекты и т.д.)
  • Приоритетный способ управлять файлами — это использование конструкции with.

with open("hello.txt") as hello_file:
    for line in hello_file:
        print line


Подобные циклу for объекты, которые не поддерживают конструкцию with и используют contextlib.closing()

import contextlib

with contextlib.closing(urllib.urlopen("http://www.python.org/")) as front_page:
    for line in front_page:
        print line


Старый код, написанный под Python 2.5, может подключить возможность использования конструкции with при помощи импорта "from __future__ import with_statement".

Комментарий TODO

Используйте комментарий TODO для временного кода, краткосрочной заплатки, либо хорошего, но не идеального решения
Комментарии-TODO должны включать в себя строку TODO в начале каждого комментария, следующую за имменем, электронным адресом или другим идентификатором того, что лучше сможет обеспечить решение проблемы, указанной в скобках. Точка необязательна. Комментарий, объясняющий что там должно происходить, не требуется. Главная цель — это общий формат для TODO, который позволит быстро найти определенного челвоека, который сможет обеспечить более детальное описание проблемы. TODO не является залогом того, что человек решит данную проблему. Таким образом, когда Вы создаете TODO, он почти всегда содержит только Ваше имя.
# TODO(kl@gmail.com): Use a "*" here for string repetition.
# TODO(Zeke) Change this to use relations.
If your TODO is of the form "At a future date do something" make sure that you either include a very specific date ("Fix by November 2009") or a very specific event
("Remove this code when all clients can handle XML responses.").


Оформление импортов

Каждый импорт должен быть на отдельной строке
Хорошо:

import os
import sys

Плохо:

import os, sys


Импорты всегда располагаются в начале файла, сразу после комментариев уровня модуля, строк документации, перед объявлением констант и
объектов уровня модуля. Импорты должны быть сгруппированы в порядке от самых простых до самых сложных:
  • Импорты из стандартной библиотеки
  • Сторонние импорты
  • Импорты из библиотек Вашего приложения

Наряду с группированием импорты должны быть отсортированы лексикографически, нерегистрозависимо, согласно полному пути до каждого модуля.
import foo
from foo import bar
from foo.bar import baz
from foo.bar import Quux
from Foob import ar


Конструкции

В основном только одна конструкция в одной строке
Однако вы можете расположить результат текста на той же строке, на которой у Вас рапологается условие. Но это можно сделать только в том случае, когда все выражение помещается на одной строке. В частности, Вы никогда не сможете это сделать с конструкцией try/except, т.к. try и except не могут находиться в одной строке, и Вам доступно помещение в одну строку только с конструкцией if БЕЗ else.
Хорошо:

if foo: bar(foo)


Плохо:

if foo: bar(foo)
else:   baz(foo)

try:               bar(foo)
except ValueError: baz(foo)

try:
      bar(foo)
except ValueError: baz(foo)


Контроль доступа

Скрытый текст
Если функция-геттер была бы простой, Вы должны были бы использовать общедоступные переменные для функций-геттеров, чтобы избежать
дополнительных расходов ресурсов на внутренние вызовы Python. Когда добавлено много функциональности, вы можете использовать свойства,
дабы следовать целостности интерфейса. С одной стороны, если функция-геттер более сложная, или скорость доступа к переменной очень важна, Вы должны использовать вызовы фукнций(см. следующий раздел гайдлайна), такие как get_foo() и set_foo(). Если поведение позволяет доступ через свойство, то не привязывайте к нему новый геттер. Любой код, который все еще пытается получить доступ к переменной при помощи старого метода, должен явно падать, дабы Вы смогли получить предупреждение о том, что сложность доступа изменилась.

Именование

module_name, package_name
module_name, package_name, ClassName, method_name, ExceptionName, function_name, GLOBAL_CONSTANT_NAME, global_var_name, instance_var_name, function_parameter_name, local_var_name.

Имена, которых следует избегать:
  • Односимвольные имена, исключая счетчики, либо итераторы
  • Минусы в именах модулей и пакетов.
  • Двойные подчеркивания (в начале и конце имен) — зарезервированы для языка.


Конвенция именования
  • «Внутренний» — означает внутренний для модуля, защищенный или приватный класс.
  • Подстановка ведущего подчеркивания (_) поддерживается для защищенных переменных модуля и функций (которые не импортируются при import * from)
  • Подстановка двойного ведущего подчеркивания (__) к имени переменной или метода эффективно делает их приватными для данного класса (использует добавление к имени). Помещение связанных классов и уровней верхнего уровня вместе в модуле. В отличие от Java, не нужно ограничивать себя в создании одного класса в одном модуле.
  • Используйте верблюжью нотацию для именования классов, а нотацию с прописными буквами и подчеркиваниями для имен модулей. Хотя существует много модулей использующих для именования верблюжью нотацию, но так не рекомендуется делать, ведь это может вводить в заблуждение, т.к. модуль называется в честь класса. (погодите, не писал ли я import StringIO или from StringIO import StringIO?)


Стили основаны на рекоммендациях Гвидо:
Тип Внешний Внутренний
Пакеты lower_with_under
Модули lower_with_under _lower_with_under
Классы CapWords _CapWords
Исключения CapWords
Функции lower_with_under() _lower_with_under()
Глобальные/Внутриклассовые константы CAPS_WITH_UNDER _CAPS_WITH_UNDER
Глобальные/Внутриклассовые переменные lower_with_under _lower_with_under
Переменные экземпляра класса lower_with_under _lower_with_under (protected) or __lower_with_under (private)
Имена методов lower_with_under() _lower_with_under() (protected) or __lower_with_under() (private)
Аргументы функций/методов lower_with_under
Локальные переменные lower_with_under


Main

Даже когда файл создавался для того, чтобы быть импортированным..
Даже когда файл создавался для того, чтобы быть импортированным, обычный импорт не должен иметь побочных эффектов в виде исполнения функциональной части скрипта. Основная функциональность должна быть заложена в функции main().
В Python, pychecker, pydoc и юнит тестах требуются импортируемые модули. Ваш код должен всегда проверять if __name__ == '__main__' перед исполнением, что означает, что Ваш модуль не будет полностью исполнен при импортировании его в другую программу.
def main():
      ...

if __name__ == '__main__':
    main()

Весь код верхнего уровня будет исполнен, когда модуль будет импортирован. Будьте осторожны и не вызывайте функции, не создавайте объекта и не проводите другого вида операции, которые не должны будут выполняться, когда файл подвергнется проверке с помощью pychecker или будет собираться документация при помощи pydoc.

Заключительные слова

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

Версия: 2.48
Amit Patel
Antoine Picard
Eugene Jhong
Jeremy Hylton
Matt Smart
Mike Shields


От переводчика


ЧАВО

  • PDF-версия перевода в скором времени будет выложена ссылкой в обоих статьях, так что не промахнетесь:)
  • Нет, я не перевожу комментарии. Я считаю (лично мое мнение), что комментарии в коде должны быть на английском.

Благодарности

Огромное спасибо squaii за ревью и поиск ошибок.

Заключение

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

P.S.: И помните:

Only registered users can participate in poll. Log in, please.
Как Вы думаете, стоит ли переводить статьи по программированию на русский язык?
54.26% Конечно стоит! Ведь не все легко читают на английском439
35.97% Стоит, хотя бы ради поддержания и развития русской IT-культуры291
8.41% Не стоит, хотя бы потому, что каждый программист должен знать английский на уровне свободного чтения68
1.36% Не стоит, потому, что и так всё ясно — без мнения разных там гуглов11
809 users voted. 74 users abstained.
Tags:
Hubs:
+53
Comments52

Articles