Автостопом по Python.

Идиомы.

Несмотря на то что обычно существует всего один очевидный способ решить задачу, код Python, написанный с помощью идиом (питонский код), может поначалу казаться неочевидным для новичков (если только они не голландцы[39]). Поэтому вам необходимо освоить хорошие идиомы.

Распаковка.

Если вы знаете длину списка или кортежа, можете присвоить имена их элементам с помощью распаковки. Поскольку вы можете указать количество разбиений строки для функций split() и rsplit(), правую сторону выражения присваивания можно разбить только один раз (например, на имя файла и расширение), а левая сторона может содержать оба места назначения одновременно, в правильном порядке. Например, так:

>>> filename, ext = "my_photo.orig.png".rsplit(".", 1).

>>> print(filename, "is a", ext, "file.").

My_photo.orig is a png file.

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

A, b = b, a.

Вложенная распаковка также работает:

A, (b, c) = 1, (2, 3).

В Python 3 в PEP 3132 (https://www.python.org/dev/peps/pep-3132/) был представлен новый метод расширенной распаковки:

A, *rest = [1, 2, 3].

# a = 1, rest = [2, 3].

A, *middle, c = [1, 2, 3, 4].

# a = 1, middle = [2, 3], c = 4.

Игнорирование значения.

Если вам необходимо присвоить какое-то значение во время распаковки, но сама переменная не нужна, воспользуйтесь двойным подчеркиванием (__):

Filename = 'foobar.txt'.

Basename, __, ext = filename.rpartition('.').

Автостопом по Python. Многие руководства по стилю для Python рекомендуют использовать одинарное подчеркивание (_) для подобных переменных вместо двойного (__), о котором говорится здесь. Проблема в том, что одинарное подчеркивание зачастую применяется как псевдоним для функции gettext.gettext() и как интерактивное приглашение сохранить значение последней операции. Двойное подчеркивание выглядит точно так же прозрачно и почти так же удобно, снижает риск случайного переписывания переменной с именем «_» в обоих сценариях.

Создание списка длиной N, состоящего из одинаковых значений.

Используйте оператор списка Python * для того, чтобы создать список, состоящий из одинаковых неизменяемых элементов:

>>> four_nones = [None] * 4.

>>> print(four_nones).

[None, None, None, None].

Одинаковые объекты должны иметь одинаковые значения хэша. В документации к Python содержится более подробная информация.

Однако будьте осторожны при работе с изменяемыми объектами: поскольку списки изменяемы, оператор * создаст список, состоящий из N ссылок на него самого, и это вряд ли вас устроит. Поэтому используйте списковое включение:

Плохой код Хороший код
>>> four_lists = [[]] * 4 >>> four_lists = [[] for __ in range(4)]
>>> four_lists[0].append("Ni") >>> four_lists[0].append("Ni")
>>> print(four_lists) >>> print(four_lists)
[['Ni'], ['Ni'], ['Ni'], ['Ni']] [['Ni'], [], [], []]

Распространенная идиома для создания строк состоит в том, чтобы использовать функцию str.join() для пустой строки. Данная идиома может быть применена к спискам и кортежам:

>>> letters = ['s', 'p', 'a', 'm'].

>>> word = ''.join(letters).

>>> print(word).

Spam.

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

Для примера рассмотрим следующий код:

>>> x = list(('foo', 'foo', 'bar', 'baz')).

>>> y = set(('foo', 'foo', 'bar', 'baz')).

>>>

>>> print(x).

['foo', 'foo', 'bar', 'baz'].

>>> print(y).

{'foo', 'bar', 'baz'}.

>>>

>>> 'foo' in x True.

>>> 'foo' in y True.

Даже несмотря на то что обе булевых проверки на наличие в списке и множестве выглядят идентично, а foo in y учитывает тот факт, что множества (и словари) в Python являются хэш-таблицами[40], производительность для этих двух примеров будет различной. Python должен пройти по каждому элементу списка в поисках совпадения, на что уходит много времени (это заметно при увеличении размера коллекций). Но поиск ключей во множестве может быть выполнен быстро с помощью поиска по хэшу. Кроме того, множества и словари не могут содержать повторяющихся записей и идентичных ключей. Для получения более подробной информации поинтересуйтесь семинаром на эту тему на ресурсе Stack Overflow (http://stackoverflow.com/questions/513882).

Контексты с гарантией безопасности по исключениям.

Зачастую блоки try/finally используются для управления ресурсами вроде файлов или блокировок потоков в случае генерации исключений. В PEP 343 (https://www.python.org/dev/peps/pep-0343/) представлены оператор with и протокол управления контекстом (в версиях 2.5 и выше) — идиома, позволяющая заменить блоки try/finally на более читаемый код. Протокол состоит из двух методов, __enter__() и __exit__(), которые при реализации для объекта позволяют использовать этот объект в операторе with, например так:

>>> import threading.

>>> some_lock = threading.Lock().

>>>

>>> with some_lock:

… ····# Создать Землю 1, запустить ее на десять миллионов лет…

… ····print(

… ········"Look at me: I design coastlines.\n".

… ········"I got an award for Norway.".

… ····)

…

Раньше это выглядело бы так:

>>> import threading.

>>> some_lock = threading.Lock().

>>>

>>> some_lock.acquire().

>>> try:

… ····# Создать Землю 1, запустить ее на десять миллионов лет…

… ····print(

… ········"Look at me: I design coastlines.\n".

… ········"I got an award for Norway.".

… ····)

… finally:

… ····some_lock.release().

Модуль стандартной библиотеки contextlib (https://docs.python.org/3/library/contextlib.html) предоставляет дополнительные инструменты, которые помогают преобразовать функции в менеджеры контекстов, навязать вызов метода close(), подавить исключения (в Python 3.4 и выше) и перенаправить стандартные потоки вывода и ошибок (в Python 3.4, 3.5 и выше). Рассмотрим пример использования функции contextlib.closing():

>>> from contextlib import closing.

>>> with closing(open("outfile.txt", "w")) as output:

… ····output.write("Well, he's…he's, ah…probably pining for the fjords.").

…

56.

Но поскольку методы __enter__() и __exit__() определены для объекта, который отвечает за ввод/вывод для файла[41], мы можем использовать это выражение непосредственно, не закрывая файл самостоятельно:

>>> with open("outfile.txt", "w") as output:

····output.write(

········"PININ' for the FJORDS?!?!?!? ".

········"What kind of talk is that? look, why did he fall ".

········"flat on his back the moment I got 'im home?\n".

····)

…

123.