Pull to refresh

Code Like a Pythonista: Idiomatic Python (part0)

Reading time 12 min
Views 28K
Original author: David Goodger
Kaa, the Python
От переводчика:

Я только начал изучать Python. С самого первого знакомства язык порадовал симпатичными конструкциями и синтаксически-гарантированной удобностью к чтению и пониманию кода.
В процессе освоения, при написании своего кода, бывает, сомневаюсь в правильности выбранных способов с точки зрения Python-way ( PEP 8 — Style Guide for Python Code, если угодно). Для вникания в идеологию программирования, в Python-сообществе кроме исчерпывающей документации, ко всеобщей радости, накоплено уже немало вспомогательных материалов, таких как статья Python Tips, Tricks, and Hacks, перевод которой недавно появился на Хабре
Мне понравилась статья Дэвида Гуджера «Пиши код, как настоящий Питонист: идиоматика Python» (David Goodger «Code Like a Pythonista: Idiomatic Python»). Для лучшего её усвоения решил оформить (в силу умения) полноценный перевод, потом показалось здравой идеей поделиться с Хабром.
Пока работал над переводом, пришло понимание, что статья существенно больше, чем показалась при прочтении ее в оригинале, поэтому постить буду частями, чтобы не выпасть из формата Хабра-статьи.
Продолжение и окончание перевода.





Пиши код, как настоящий Питонист: идиоматика Python


David Goodger
goodger@python.org
http://python.net/~goodger

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

Creative Commons
Attribution/Share-Alike (BY-SA) license.
Обо мне:
  • живу в Монреале,
  • отец двоих детей, муж одной женщины
  • Python-программист полный рабочий день (a full-time Python programmer),
  • автор проекта Docutils и reStructuredText,
  • редактор Python Enhancement Proposals (or PEPs),
  • организатор PyCon 2007, и кафедры PyCon 2008,
  • член Python Software Foundation,
  • директор фонда в прошлом году, и секретарь в нынешнем.

Этот туториал я представил на конференции PyCon 2006 (called Text & Data Processing), я был удивлен реакцией на некоторые методы, которые я использовал и считал их общеизвестными. Но многие слушатели были в неведении о методах, которые используются Python-программистами, не задумываясь.
Многие из вас могли видеть некоторые идиомы и методы раньше. Хочется верить, что вы также узнаете несколько приемов, которые не видели раньше и может быть узнаете кое-что новое о тех, что уже вам знакомы.


Дзен Python (1)


Это основные принципы Python, но в расширенной интерпретации. Чувство юмора просто необходимо для правильного их понимания.
Если вы используете язык программирования, названного в честь комедийной скетч-труппы, лучше, чтобы у вас было чувство юмора.

Красивое лучше безобразного.
Явное лучше неявного.
Простое лучше сложного.
Сложное лучше запутанного.
Плоское лучше вложенного.
Разреженное лучше плотного.
Читаемость важна.
Исключительные случаи не достаточно исключительны, чтобы нарушать правила.
Хотя практичность важнее красоты.
Ошибки не должны проходить молча.
Если не заглушены явно.
...


Дзен Python (2)


При неоднозначности откажитесь от искушения догадаться.
Должен существовать один и, желательно, только один, очевидный, способ сделать это.
Хотя этот путь может оказаться не очевидным сначала, если только вы не из Голландии (видимо имеется ввиду Гвидо ван Россум — прим. перев., спасибо sgtpep).
Сейчас лучше, чем никогда.
Хотя никогда зачастую лучше, чем прямо сейчас.
Если реализацию сложно объяснить, то это плохая идея.
Если реализацию легко объяснить, это, возможно, хорошая идея.
Пространства имен — грандиозная идея, так давайте сделаем еще больше пространств имен!

—Тим Питерс (Tim Peters)
Эта «поэма» создавалась в шутку, но в действительности содержит много истин относительно философии Python.
В течение долгого времени Pythoneer Тим Питерс переложил принципы BDFL-руководства в 20 кратких афоризмов, только 19 из которых были записаны.
http://www.python.org/dev/peps/pep-0020/

Вы можете сами решить для себя, кто вы: «Pythoneer» или «Pythonista». Эти слова имеют сопутствующие значения.

Когда в сомнении:
import this<br/>

Попробуйте это в интерактивном интерпретаторе Python:
>>> import this<br/>

Вот другое «пасхальное яйцо»:
>>> from __future__ import braces<br/>
  File "<stdin></stdin>", line 1<br/>
SyntaxErrornot a chance<br/>

Что за сборище комедиантов! :-)


Стиль кодинга: читаемость важна (Coding Style: Readability Counts)


Программы должны быть написаны для чтения людьми и только по случайности для исполнения машинами.
—Abelson & Sussman, Structure and Interpretation of Computer Programs («Структура и интерпретация компьютерных программ»)

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

PEP 8: Руководство по стилю Python-кода


Стоит почитать:
http://www.python.org/dev/peps/pep-0008/

PEP = Python Enhancement Proposal (Предложения по развитию Python)
PEP является документом, представляющим информацию Python-сообществу, описывающим новые фичи Python-а, его процессов или окружения.
Python-сообщество имеет собственные стандарты, описывающие, как должен выглядеть исходный код, изложенные в PEP 8. Эти стандарты отличаются от других, таких как C, C++, C#, Java, VisualBasic и др.
Поскольку отступы и пробелы исключительно важны в Python, Руководство по стилю для Python-кода использует отступы по стандарту. Это мудро, что руководство придерживается руководства!.. Большинство опенс-соурс проектов и (надо надеяться) домашних тоже, следуют руководству довольно строго.


Пробелы 1


  • 4 пробела на один уровень отступа.
  • Нет табуляциям.
  • Никогда не смешивайте табуляцию и пробелы.
    Это точно поддерживается в IDLE и Emacs Python mode. Другие редакторы тоже могут уметь это.
  • Одна пустая строка между функциями.
  • Две пустых строки между классами.


Пробелы 2


  • Добавьте пробел после "," в словарях, списках, кортежах, списках аргументов, и после ":" в словарях, но не перед.
  • Вставьте пробелы вокруг присваиваний и сравнений (кроме списков аргументов).
  • Никаких пробелов внутри круглых скобок или перед списком аргументов.
  • Никаких пробелов в начале и конце строк документации.

def make_squares(key, value=0‌):<br/>
    """Return a dictionary and a list..."""<br/>
    d = {key: value}<br/>
    l = [key, value]<br/>
    return d, l<br/>


Именование


  • слитно_строчными для функций, методов, атрибутов
  • joined_lower или ВСЕ_КАПСОМ для констант
  • StudlyCaps для классов
  • camelCase только чтобы соответствовать предопределенным соглашениям
  • Атрибуты: interface, _internal, __private

    Попробуйте избегать __private форму. Я никогда не использую это. Поверьте мне. Если вы это используете, вы пожалеете об этом позже.
    Объяснение:

    Люди, пришедшие после C++/Java изначально склонны к неправильному и чрезмерному использованию этой «фичи». __private имена работают не так, как в Java или C++. Это просто «запускает» механизм преобразования имени(name mangling), что позволяет предотвратить случайные коллизии пространств имен в подклассах: MyClass.__private становится MyClass._MyClass__private. (Заметьте, что это работает даже для подклассов с тем же самым именеем, что у суперкласса, например подкласс, определенный в другом модуле.) Таким образом, возможен доступ к __private именам снаружи класса, однако неудобно и хрупко (это добавляет зависимость от точного имени суперкласса).
    Проблема в том, что автор класса закономерно может думать: «Это имя атрибута/метода должно быть приватным, доступным только внутри этого определения класса» и использовать __private объявление. Но позже, пользователь этого класса может создать подкласс, в котором ему законно нужен доступ к этому имени. Для этого суперкласс может быть изменен (что может быть затруднительно или невозможно), или в коде подкласса применить преобразование имени (mangled names) (что безобразно и хрупко в лучшем случае).

    Это концепция Python-а: «Мы все тут взрослые».
    Если вы используете __private форму, от кого вы защищаете атрибут? Это ответственность подклассов — использовать атрибуты суперкласса правильно, и ответственность суперкласса — документировать свои атрибуты верно.
    Для этих целей лучше использовать начинающееся с одного подчеркивания объявление, _internal. Это не преобразует имена; просто говорит другим «быть осторожнее с этим, это реализация внутренних механизмов; не трогайте это, если не понимаете это полностью». Все же это только соглашение.
    Есть несколько хороших объяснений в ответах здесь:



Длинные строки кода (Lines) и их продолжения


Держите длину строк кода в пределах 80 символов.
Используйте неявные продолжения строк внутри скобок:
def __init__(self, first, second, third,<br/>
             fourth, fifth, sixth):<br/>
    output = (first + second + third<br/>
              + fourth + fifth + sixth)<br/>

Используйте бэкслеш:
VeryLong.left_hand_side \<br/>
    = even_longer.right_hand_side()<br/>

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

Длинные строки (Strings)


Cмежные литеральные строки (символы) конкатенируются парсером:
>>> print 'o' 'n' "e"<br/>
one<br/>

Пробелы между литерами не требуются, но помогают сделать код более читаемым. Может быть использован любой тип кавычек:
>>> print 't' r'\/\/' """o"""<br/>
t\/\/o<br/>

Строки, начинающиеся с «r» являются «сырыми» (raw). Бэкслеши не считаются эскейп-симовлами в сырых строках. Они используются для регулярных выражений и путей в файловых системах Windows.
Заметьте, что именованные строковые объекты не конкатенируются:
>>> a = 'three'<br/>
>>> b = 'four'<br/>
>>> a b<br/>
  File "<stdin>", line 1<br/>
    a b<br/>
      ^<br/>
SyntaxError: invalid syntax<br/>

Это потому что автоматическая конкатенация — фича парсера Python (синтаксического анализатора), а не интерпретатора. Вы должны использовать оператор "+" для объединения строк в рантайме.

text = ('Long strings can be made up '<br/>
        'of several shorter strings.')<br/>

Круглые скобки позволяют неявное объединение строк кода.
Для задания многострочных строковых значений используйте тройные кавычки:

"""Triple<br/>
double<br/>
quotes"""
<br/>

'''\<br/>
Triple<br/>
single<br/>
quotes\<br/>
'''
<br/>

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


Составные операторы



Хорошо:
if foo == 'blah':<br/>
    do_something()<br/>
do_one()<br/>
do_two()<br/>
do_three()<br/>

Плохо:
if foo == 'blah': do_something()<br/>
do_one(); do_two(); do_three()<br/>

Пробелы и отступы — полезные визуальные индикаторы программного потока. Отступ второй строки «Хорошего» кода выше показывает читателю выполнение чего-то по условию, в то время как отсутствие отступа в «Плохо» прячет «if» условие.
Множество операторов на одной строке кода — основной грех. В Python, важна читаемость.



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



Строки документации = Как использовать код

Комментарии = Почему (разумное обоснование) и как код работает
Строки документации объясняют как использовать код, и для пользователей вашего кода. Использование строк документации:
  • Объясните цель функции, даже если это кажется вам очевидным, потому что это может оказаться неочевидным кому-нибудь позже.
  • Опишите ожидаемые параметры, возвращаемые значения, и любые вызываемые исключения.
  • Если метод плотно связан с единственным вызовом, создайте какую-нибудь связь с вызывающим оператором (хотя будьте осторожны, потому что вызывающий оператор может измениться впоследствии).

Комментарии объясняют почему и нужны для майнтайнеров вашего кода. Примеры включают заметки для себя, как:
# !!! BUG: ...<br/>
<br/>
# !!! FIX: This is a hack<br/>
<br/>
# ??? Why is this here?<br/>

Обе этих группы включают Вас, так что пишите хорошие строки документации и комментарии!
Строки документации используются в интерактивном режиме (help()) и для систем авто-документации.

Ложные комментарии и строки документации хуже, чем совсем ничего. Так сохраняйте их сразу! Когда вы делаете изменения, убедитесь, что комментарии и строки документации соответствуют коду и не противоречат ему.
В PEP есть целое соглашение о строках документации, PEP 257, «Docstring Conventions»:
http://www.python.org/dev/peps/pep-0257/



Практичность побеждает чистоту



Глупая согласованность — маленький монстр недалекого ума (A foolish consistency is the hobgoblin of little minds).
—Ralph Waldo Emerson

(hobgoblin: Что-то, вызывающее суеверный страх)
(примеч. перев.: Как я понял, смысл такой: «Научи дурака богу молиться — он себе лоб расшибет»)
Всегда есть исключения. Из PEP 8:
Очень важно, когда возникнет противоречие — бывает так, что следование руководству просто не приемлемо. Когда сомневаетесь, используйте решение, наилучшее на ваш взгляд. Посмотрите на другие примеры и решите, какая точка зрения лучше. И не стесняйтесь спрашивать!
Две хороших причины ломать принятые правила:
  1. При следовании правилам код становится менее удобочитаемым, даже для кого-нибудь, кто привык следовать правилам.
  2. Чтобы соответствовать окружающему коду, тоже можно нарушать (это может быть по историческим причинам) — хотя это может оказаться возможностью навести порядок в чьей-то путанице (в тру-ХР стиле (eXtreme Programming — примеч. перев.)).



… но практичность не должна нарушать чистоту постоянно!

Попурри из идиом


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



Обмен значений


В других языках:
temp = a<br/>
= b<br/>
= temp<br/>

В Python:
b, a = a, b<br/>

Возможно, вы видели это прежде. Но знаете ли вы, как это работает?
  • Запятая является синтаксисом конструктора кортежа.
  • Справа создается кортеж (упаковка кортежа).
  • Кортеж слева является целью присваивания (распаковка кортежа).


Правая сторона распакована по именам в кортеж с левой стороны.
Еще примеры распаковки:
>>> l =['David''Pythonista''+1-514-555-1234']<br/>
>>> name, title, phone = l<br/>
>>> name<br/>
'David'<br/>
>>> title<br/>
'Pythonista'<br/>
>>> phone<br/>
'+1-514-555-1234'<br/>

Полезно в циклах для обработки структурированных данных:

Выше мы создали список (David's info). people — список людей, содержащий два элемента, каждый из которых является списком из трех элементов.
>>> people = [l, ['Guido''BDFL''unlisted']]<br/>
>>> for (name, title, phone) in people:<br/>
...     print name, phone<br/>
...<br/>
David +1-514-555-1234<br/>
Guido unlisted<br/>

Каждый элемент people был распакован в кортеж вида (name, title, phone).
Только не забудьте соответствровать структуре слева и справа!
>>> david, (gname, gtitle, gphone) = people<br/>
>>> gname<br/>
'Guido'<br/>
>>> gtitle<br/>
'BDFL'<br/>
>>> gphone<br/>
'unlisted'<br/>
>>> david<br/>
['David''Pythonista''+1-514-555-1234']<br/>



Еще о кортежах


Мы видели, как запятая была конструктором кортежа, без круглых скобок. Пример:
>>> 1,<br/>
(1,)<br/>

Интерпретатор Python-а показывает скобки для ясности, и я рекомендую вам тоже их использовать:

>>> (1,)<br/>
(1,)<br/>

Не забывайте запятую!
>>> (1)<br/>
1<br/>

В кортеже из одного элемента требуется конечная запятая; в 2+-кортежах, конечная запятая опциональна. В 0-кортежах, или пустых кортежах, пара круглых скобок является укороченным синтаксисом:
>>> ()<br/>
()<br/>

>>> tuple()<br/>
()<br/>

Общепринятая опечатка — оставить запятую в коде, даже если вы не хотите создавать кортеж. Её легко можно пропустить в вашем коде:
>>> value = 1,<br/>
>>> value<br/>
(1,)<br/>

Таким образом, если вы видите кортеж, где не ждали, поищите запятую!


Интерактивное "_"



Это действительно полезная фича, удивительно, что немногие знают о ней.

В интерактивном режиме интерпретатора, всякий раз, когда вы пробуете выражение или вызов функции, результат сохраняется во временной переменной, _ (подчеркивание):

>>> 1 + 1<br/>
2<br/>
>>> _<br/>
2<br/>

_ хранит последнее напечатанное командой print выражение. Это удобно!
Но работает только в интерактивном режиме интерпретатора, не в модулях.

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

>>> import math<br/>
>>> math.pi / 3<br/>
1.0471975511965976<br/>
>>> angle = _<br/>
>>> math.cos(angle)<br/>
0.50000000000000011<br/>
>>> _<br/>
0.50000000000000011<br/>



Составление строк из подстрок


Начнем со списка из строк:
colors = ['red''blue''green''yellow']<br/>

Мы хотим соединить все строки вместе в одну большую строку. Особенно, когда подстрок много…

не делайте так:
result = ''<br/>
for s in colors:<br/>
    result += s<br/>

Это очень неэффективно.

Это ужасно съедает память и ухудшает производительность. «Суммирование» вычисляет, сохраняет и потом уничтожает объект на каждом промежуточном шаге.


Вместо этого, делайте так:
result = ''.join(colors)<br/>

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



Составление строк, вариация 1


Вот некоторые способы использования метода join().

Если вы хотите расставить пробелы между вашими подстроками:
result = ' '.join(colors)<br/>

Или запятые и пробелы:
result = ', '.join(colors)<br/>

В общем случае:
colors = ['red''blue''green''yellow']<br/>
print 'Choose'', '.join(colors[:-1]), \<br/>
      'or', colors[-1]<br/>

Чтобы составить грамматически правильное предложение, мы хотим поставить запятую между всеми значениями, а перед последним слово «or». Тут помогает синтаксис срезов. «Срез до -1» ([:-1]) выдает индекс предпоследнего элемента который мы будем присоединять через запятую с пробелом.
Конечно, этот код не захочет работать в случаях, когда длина списка равна 0 или 1.

Вывод:
Choose red, blue, green or yellow<br/>


Составление строк, вариация 2


Если вам нужно применить функцию для генерации подстрок:
result = ''.join(fn(i) for i in items)<br/>

Здесь используется выражение-генератор, которое мы рассмотрим позже.

Если вам понадобилось вычислить подстроки пошагово, соберите их сначала в список:
items = []<br/>
...<br/>
items.append(item)  # many times<br/>
...<br/>
# items is now complete<br/>
result = ''.join(fn(i) for i in items)<br/>

Мы собрали части в список так, что мы можем применить теперь метод строк join
для большей эффективности.





Вторая часть перевода



Tags:
Hubs:
+33
Comments 30
Comments Comments 30

Articles