Доброго времени суток. Вот и пришло время для публикации второй части так понравившегося многимам перевода стайл гайда для языка Python от компании Google, (первая часть бережно хранится хабром). Теперь мы коснемся напрямую форматирования исходного кода на языке программирования Python. Как известно, чистота — залог здоровья, а чистота программного кода — залог уважения коллег и (в идеале) поощрения от кого-нибудь свыше. Вообще, Python сам по себе является хорошо читаемым языком, и даже синтаксис данного языка призывает к порядку в коде (и, как следствие — в голове). Но каждый из нас сам себе документатор и сам себе творец оформления. А как уже говорилось однажды — ко мнению авторитетных товарищей нельзя не прислушиваться. Итак, вторая часть Google Python Style Guide — Python Style Rules ждет Вас под катом.
Python Style Rules
Точки с запятой
Длина строки
Исключения
- Длинные строки импорта
- 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
Обратите внимание на отступ элемента в строке выше (смотрите раздел про отступы, чтобы получить разъяснения).
Скобки
Хорошо:
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)
Отступы
Хорошо:
# 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,
}
Строка #! (хэш-бэнг)
Комментарии
Строки документации.
Python имеет уникальный стиль комментирования — строки документации. Строка документации это строка, которая является первой конструкцией в пакете, модуле, классе или функции. Такие строки могут быть экспортированы автоматически с помощью атрибута объекта __doc__ и используются pydoc-ом. (Попробуйте запустить pydoc на своем модуле, чтобы увидеть как это выглядит.) Наше соглашение по строкам документации велит использовать три двойные кавычки для обрамления такой строки. Строки документации должны быть организованы как суммарная строка (одна физическая строка), сносящаяся по кол-ву символов, знаку вопроса или восклицательному знаку, следующим за пустой строкой, а затем остальные строки документации с позиции курсора в качестве первой кавычки первой строки. Ниже описано еще больше информации по оформлению строк документирования.
Модули
Каждый файл должен содержать в себе шаблон лицензии. Выберите подходящий шаблон лицензии для вашего проекта.(Например, Apache 2.0, BSD, LGPL, GPL).
Функции и методы
Используемый в этом разделе термин (функция) относится к методам, функциям и генераторам.
Функция должна иметь строку документации во всех случаях, кроме описанных ниже:
- Не видима снаружи модуля
- Очень короткая
- Очевидная (легко читаемая)
Строка документирования должна давать достаточно информации, чтобы оформить вызов функции без чтения ее исходного кода. Строка документирования должна описывать синтаксис вызова функции и ее семантику, но не должна описывать ее реализацию. Для хитрого кода комментарии внутри исходного кода более предпочтительны, чем строки документации. Определенные аспекты функции должны быть задокументированы в специальных секциях, перечисленных ниже. Каждая секция начинается со строки заголовка, которая заканчивается точкой. Секции должны иметь отступ в два пробела, за исключением заголовочной.
Args:
Перечислите каждый параметр по имени. Описание должно следовать сразу за именем и быть разделено точкой и пробелом. Если описание слишком длинное, чтобы уместить его в в 80 символов, используйте подвешенный отступ в 2 или 3 пробела (будьте последовательны в оформлении всего файла).
Описание должно ссылаться на требуемый тип(ы) и назначение аргумента. Если функция позволяет *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
Классы
Хорошо:
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(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
Конструкции
Хорошо:
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(). Если поведение позволяет доступ через свойство, то не привязывайте к нему новый геттер. Любой код, который все еще пытается получить доступ к переменной при помощи старого метода, должен явно падать, дабы Вы смогли получить предупреждение о том, что сложность доступа изменилась.
Именование
Имена, которых следует избегать:
- Односимвольные имена, исключая счетчики, либо итераторы
- Минусы в именах модулей и пакетов.
- Двойные подчеркивания (в начале и конце имен) — зарезервированы для языка.
Конвенция именования
- «Внутренний» — означает внутренний для модуля, защищенный или приватный класс.
- Подстановка ведущего подчеркивания (_) поддерживается для защищенных переменных модуля и функций (которые не импортируются при 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
В 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-специалистов (и не только). Всем добра!
Автор: Lovesuper