Перейти к содержанию

Python/Учебник Python 2.6

Материал из Викиучебника — открытых книг для открытого мира
Документация Python
Эта статья — часть Документации по языку Python

Учебник Python 2.6

[править]
Автор языка Гвидо ван Россум (Guido van Rossum)
Владелец Python Software Foundation
E-mail docs@python.org
Исходный текст http://docs.python.org/tutorial/index.html
Редактор, Автор Фред Л. Дрейк мл. (Fred L. Drake, Jr. ) и другие
Релиз 2.6.1
Версия от 22 декабря 2008



Описание

[править]

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

Интерпретатор Python и разрастающаяся стандартная библиотека находятся в свободном доступе в виде исходников и двоичных файлов для всех основных платформ на официальном сайте Python http://www.python.org и могут распространяться без ограничений. Кроме этого на сайте содержатся дистрибутивы и ссылки на многочисленные модули сторонних разработчиков для языка Python, различные программы и инструменты, а также дополнительная документация.

Интерпретатор Python может быть легко расширен с помощью новых функций и типов данных, написанных на C/C++ (или других языках, к которым можно получить доступ из C). Также Python можно применять как язык расширений для настраиваемых приложений.

Этот учебник в свободной форме излагает основные концепции и возможности языка и системы Python. Полезно иметь интерпретатор Python под рукой, чтобы пробовать примеры на практике, Однако все примеры самодостаточны, так что учебник вполне возможно читать вне сети.

Описание стандартных объектов и модулей вы можете найти в справочнике по библиотеке Python. Руководство по языку даёт более формальное описание языка. Перед написанием расширений на C/C++ ознакомьтесь с руководством по расширению и встраиванию в интерпретатор и справочником по Python/C API. Существует также несколько книг, тщательно разбирающих язык Python.

Этот учебник не претендует на звание всеобъемлющего и не описывает каждую особенность Python и даже не описывает всех его часто использующихся особенностей. Вместо этого он знакомит читателя с наиболее заслуживающими внимания из них и даёт вам представление о стиле и привкусе языка. После прочтения учебника вы сможете писать и читать программы и модули, написанные на Python и будете готовы узнать больше о различных модулях библиотеки Python уже сами или из других источников. Например в справочнике по библиотеке Python или Глоссарий.

Разжигаем аппетит

[править]

Если вы много работаете за компьютером, то периодически обнаруживаете задачи, которые хотели бы автоматизировать. Например, вы были бы не против ускорить операцию поиска и замены большого количества текстовых файлов, или как-то своеобразно переименовать и отсортировать набор фотографий. Возможно, вам хотелось бы написать небольшую базу данных на заказ, специализированное GUI-приложение или простую игру.

Если вы профессиональный разработчик программных продуктов — вероятно, вы привыкли работать с несколькими библиотеками на C/C++/Java, но находите обычный цикл написания/компиляции/тестирования/перекомпиляции кода чересчур медленным. Возможно, вы пишете набор тестов для такой библиотеки и процесс написания тестирующего кода воспринимается утомительным. Или же вы написали программу, которая должна использовать специальный язык для расширений и не хотите проектировать и разрабатывать полностью новый язык для вашего приложения.

Python — именно тот язык, который вам подойдёт.

Вы можете написать сценарий интерпретатора команд для Unix или использовать пакетные файлы Windows для некоторых из этих задач, но сценарии интерпретаторов команд хороши лишь для перемещения файлов и замены текстовых данных — они вряд ли подойдут для написания приложений, снабженных графическим интерфейсом, или игр. Вы можете написать программу на C/C++/Java, но разработка может занять довольно много времени — даже на то, чтобы получить первый рабочий набросок, его требуется немало. Python — проще в использовании, доступен на операционных системах Windows, Mac OS X и Unix, и поможет сделать работу намного быстрее.

Несмотря на лёгкость использования, Python — полноценный язык программирования, предлагающий много больше возможностей для структурирования и поддержки крупных программ, чем могут позволить себе сценарии интерпретаторов команд или пакетные файлы. С другой стороны, Python также предоставляет намного больше информации для отладки ошибок чем C и, будучи сверх-высокоуровневым-языком, имеет встроенные высокоуровневые типы данных — такие, как гибкие массивы и словари. Благодаря наличию обобщённых типов данных Python можно применять для более широкого круга задач, чем Awk или даже Perl, и при этом очень многие вещи сделать в Python не сложнее, чем в этих языках.

Python позволяет вам разделить вашу программу на модули, которые могут быть заново использованы в других программах на Python. Он поставляется вместе с внушительной коллекцией стандартных модулей, которые вы можете использовать в качестве фундамента ваших программ; или в качестве примеров для того, чтобы начать изучение Python. Многие из этих модулей предоставляют различную полезную функциональность: например, ввод-вывод для файлов, системные вызовы, сокеты, и даже инструменты для создания графического пользовательского интерфейса — такие, как Tk.

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

Python даёт возможность писать компактные и читабельные программы. Программы, написанные на Python отличаются большей краткостью чем эквиваленты на C, C++ или Java, по нескольким причинам:

  • высокоуровневые типы данных позволяют вам выражать сложные операции в одной инструкции;
  • группировка инструкций выполняется отступами, а не операторными скобками;
  • нет необходимости в описании переменных или аргументов.

Python расширяем: если вы знаете, как программировать на C, то вам легко будет добавить к интерпретатору новую встроенную функцию или модуль, выполнить критические операции на максимальной скорости или связать программы на Python с библиотеками, которые могут быть доступны только в бинарной форме (например, зависящие от поставщика графические библиотеки). Если вы действительно увлечены — вы можете привязать интерпретатор Python к приложению, написанному на C — чтобы использовать его как язык расширений или командный язык этого приложения.

Кстати, язык назван в честь шоу «Летающий цирк Монти Пайтона» на BBC и не имеет ничего общего с пресмыкающимися. Ссылки на трюки Монти Пайтона в документации не только позволены, но и поощряются!

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

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

Остальная часть учебника описывает различные возможности языка и системы Python на примерах — начиная с простых выражений, операторов и типов данных, рассматривая функции и модули, и заканчивая прикосновением к таким продвинутым концепциям как исключения и определённые пользователем[1] классы.

Использование интерпретатора Python

[править]

Запуск интерпретатора

[править]

Интерпретатор Python после установки располагается, обычно, по пути /usr/local/bin/python — на тех компьютерах, где этот путь доступен. Добавление каталога /usr/local/bin к пути поиска Unix-шелла (переменная PATH) позволит запустить интерпретатор набором команды

python

прямо из шелла. Поскольку выбор каталога, в котором будет обитать интерпретатор, осуществляется при его установке, то возможны и другие варианты — посоветуйтесь с вашим Python-гуру или системным администратором. (Например, путь /usr/local/python тоже популярен в качестве альтернативного расположения.)

На машинах с ОС Windows, инсталляция Python обычно осуществляется в каталог C:\Python26, но и он может быть изменён во время установки. Чтобы добавить этот каталог к вашему пути поиска, вы можете набрать в окне DOS следующую команду, в ответ на приглашение:

set path=%path%;C:\python26

При наборе символа конца файла (Ctrl-D в Unix, Ctrl-Z в Windows) в ответ на основное приглашение интерпретатора, последний будет вынужден закончить работу с нулевым статусом выхода. Если это не сработает — вы можете выйти из интерпретатора путём ввода следующих команд:

import sys; sys.exit()

Особенности редактирования строк в интерпретаторе, обычно не вызывают, особых сложностей. Те, кто установил интерпретатор на машину Unix, потенциально имеют поддержку библиотеки GNU Readline, обеспечивающей усовершенствованное интерактивное редактирование и сохранение истории. Самый быстрый, наверное, способ проверить, поддерживается ли расширенное редактирование командной строки, заключается в нажатии Ctrl-P в ответ на первое полученное приглашение Python. Если вы услышите сигнал — значит вам доступно редактирование командной строки — тогда обратитесь к Приложению об Интерактивном редактировании входных данных за описанием клавиш. Если на ваш взгляд ничего не произошло или отобразился символ ^P — редактирование командной строки недоступно — удалять символы из текущей строки возможно будет лишь использованием клавиши Backspace.

Интерпретатор ведёт себя сходно шеллу Unix: если он вызван, когда стандартный ввод привязан к устройству tty — он считывает и выполняет команды в режиме диалога; будучи вызванным с именем файла в качестве аргумента или с файлом, назначенным на стандартный ввод — он читает и выполняет сценарий из этого файла.

Другой способ запустить интерпретатор — директива python -c команда [arg] ... — при её использовании поочередно выполняются инструкции(-ция) из команды (как при использовании опции -c Unix-шелла). В связи с тем, что инструкции Python часто содержат пробелы или другие специальные для шелла символы, рекомендуется заключать команды полностью в одинарные кавычки.

Некоторые модули Python оказываются полезными при использовании их в качестве сценариев. Они могут быть запущены в этом виде командой python -m модуль [arg] ... — таким образом исполняется исходный файл модуля (как произошло бы, если бы вы ввели его полное имя в командной строке).

Обратите внимание на различие между командами python file и python <file. В последнем случае запросы на ввод из программы, такие как вызовы input() и raw_input(), удовлетворяются из самого файла. Поскольку файл уже был прочитан до конца ещё до начала выполнения программы — символ конца файла будет обнаружен программой незамедлительно. В большинстве случаев (это, чаще всего, и есть те ситуации, которых вы ожидали) вызовы удовлетворяются независимо от того, файл или устройство привязаны к стандартному вводу интерпретатора Python.

При использовании файла сценария иногда полезно иметь возможность запустить сценарий и затем войти в интерактивный режим. Это может быть сделано через указание параметра -i перед именем сценария. (Этот способ не сработает, если сценарий считывается со стандартного ввода — по той самой причине, которая описана в предыдущем абзаце).

Передача аргументов

[править]

В случае, если интерпретатору известны имя сценария и дополнительные аргументы, с которыми он вызван — все они передаются сценарию в переменной sys.argv, представляющей собой список (list) строк. Длина (length) списка — минимум единица; если не переданы ни имя сценария, ни аргументы — то sys.argv[0] содержит пустую строку. Когда в качестве имени сценария передан '-' (означает стандартный ввод), sys.argv[0] устанавливается в '-'. Если используется директива -c командаsys.argv[0] содержит '-c'. В случае, если используется директива -m модуль — то sys.argv[0] устанавливается равным полному имени модуля по расположению. Опции, обнаруженные после сочетаний -c команда или -m модуль не обрабатываются интерпретатором Python, но остаются в переменной sys.argv, дабы обеспечить возможность отслеживания в самой команде или в модуле.

Интерактивный режим

[править]

Если команды считываются с tty — говорят, что интерпретатор находится в интерактивном режиме (режиме диалога). В этом режиме он приглашает к вводу следующей команды, отобразив основное приглашение[2] (обычно это три знака «больше-чем» — >>>); в то же время, для продолжающих строк[3] выводится вспомогательное приглашение[4] (по умолчанию — три точки — ...). Перед выводом первого приглашения интерпретатор отображает приветственное сообщение, содержащее номер его версии и пометку о правах копирования:

python
Python 2.6 (#1, Feb 28 2007, 00:02:06)
Type "help", "copyright", "credits" or "license" for more information.
>>>

Продолжающие строки используются в случаях, когда необходимо ввести многострочную конструкцию. Взгляните, например, на следующий оператор if[5]:

>>> the_world_is_flat = 1
>>> if the_world_is_flat:
...     print ("Be careful not to fall off!")
...
Be careful not to fall off!

Интерпретатор и его окружение

[править]

Обработка ошибок

[править]

В случае появления ошибки интерпретатор выводит сообщение об ошибке, завершая его стеком вызовов. В интерактивном режиме он снова возвращается в состояние приглашения для ввода команд; если ввод происходит из файла — интерпретатор выходит с ненулевым статусом, сразу после распечатки стека вызовов. (Исключения, обрабатываемые в блоке except оператора try в этом контексте не считаются ошибками.) Некоторые ошибки исключительно фатальны и вызывают собой принудительное завершение работы с ненулевым статусом — это применимо к внутренним противоречиям языка и к некоторым случаям нехватки памяти. Все сообщения об ошибках выводятся в стандартный поток ошибок (error stream). Обычный вывод исполняемых команд направляется в стандартный вывод.

Нажатие клавиш прерывания процесса (обычно Ctrl-C или DEL), в ответ на приглашение в основном или вспомогательном режиме, отменяет ввод и возвращает вас к основному приглашению.[6] Символ прерывания, набранный во время выполнения какой-либо команды порождает исключение KeyboardInterrupt, которое, в свою очередь, может быть перехвачено оператором try.

Исполняемые скрипты на Python

[править]

На Unix-системах семейства BSD сценарии на Python могут быть сделаны исполняемыми, также как и шелл-сценарии, путём добавления следующей строки:

#! /usr/bin/env python

также можно писать так:

#! /usr/bin/python

(предполагается, что интерпретатор может быть найден по одному из путей, указанных в пользовательской переменной PATH) в начало сценария и установки файла в исполняемый режим. Символы #! должны быть первыми символами в файле. На некоторых платформах строка должна оканчиваться символом конца строки в стиле Unix ('\n'), а не в стиле Windows ('\r\n'). Заметьте, что символ решётки '#' используется в Python для указания начала комментария.

Исполняемый режим (или разрешение на исполнение) может быть установлен сценарию использованием команды chmod:

$ chmod +x myscript.py

У систем с операционной системой Windows нет такого понятия, как исполняемый режим. Установщик Python автоматически связывает файлы .py с файлом python.exe, таким образом двойной клик на файле Python запустит его в виде сценария. Расширение может быть и .pyw в случае, если окно консоли (которое, обычно, отображается) при запуске скрипта подавляется[7].

Кодировка исходных файлов

[править]

В исходных файлах Python возможно использовать кодировки, отличные от ASCII. Лучший способ сделать это — добавить специальный комментарий следом за строкой #! — дабы описать кодировку исходного файла:

# -*- coding: encoding -*-

Если используется это описание — все символы в этом файле будут распознаваться как имеющие соответствующую кодировку и появится возможность употреблять символы Unicode в выбранной кодировке. Список возможных кодировок представлен в Справочнике по библиотеке — в разделе, описывающем модуль codecs.

Например, для написания таких символов в кодировке Unicode, как, например, знак валюты Евро — может быть использована кодировка ISO-8859-15, где знак Евро имеет порядковый номер 164. Этот сценарий выведет значение 8346 (место в таблице Unicode, соответствующее символу Евро) и затем выполнит выход:

# -*- coding: iso-8859-15 -*-
 
 currency = u"€"
 print ord(currency)

Если ваш редактор поддерживает сохранение файлов в UTF-8 вместе с пометкой порядка байтов (также известной как BOM - Byte Order Mark) — вы можете использовать эту его возможность вместо указания кодировки. Редактор IDLE приобретает такую способность при установленном пункте меню Options/General/Default Source Encoding/UTF-8. Обратите внимание, что такие файлы не распознаются в прошлых релизах (2.2 и ранее), и также не распознаются самой операционной системой в случае файлов сценария со строкой #! в заголовке (используется только в системах Unix).

При использовании кодировки UTF-8 (путем описания кодировки или сохранения файла в ней) символы большинства языков мира могут быть использованы параллельно в строках и комментариях. Использование не-ASCII символов в идентификаторах не поддерживается. Для корректного отображения этих символов ваш редактор должен понимать, что кодировкой файла является UTF-8 и использовать шрифт, в котором будут содержаться все символы из файла.

Интерактивный файл запуска

[править]

Если вы используете Python интерактивно — часто бывает удобным выполнить некоторые стандартные команды перед запуском интерпретатора. Вы можете сделать это, установив переменную окружения с именем PYTHONSTARTUP равной имени файла, содержащего ваши команды запуска. Способ схож с использованием файла .profile в Unix-шелле.

Этот файл читается только в интерактивных сессиях, но не в случае считывания команд из сценария, и не в случае, если /dev/tty назначен как независимый источник команд (который в других случаях ведет себя сходно интерактивным сессиям). Файл исполняется в том же пространстве имён, что и исполняемые команды - поэтому объекты и импортированные модули, которые он определяет могут свободно использоваться в интерактивной сессии. Также в этом файле вы можете изменить приглашения: sys.ps1 и sys.ps2.

Если вы хотите прочитать дополнительный файл запуска из текущего каталога — вы можете использовать код вроде:

if os.path.isfile('.pythonrc.py'): execfile('.pythonrc.py')

Если вы хотите использовать файл запуска в сценарии — вам нужно будет указать это явно:

import os
filename = os.environ.get('PYTHONSTARTUP')
if filename and os.path.isfile(filename):
    execfile(filename)

Неформальное введение в Python

[править]

В приведенных далее примерах, ввод и вывод различаются присутствием и отсутствием приглашений соответственно (приглашениями являются >>> и ...): чтобы воспроизвести пример — вам нужно ввести всё, что следует за приглашением, после его появления; строки, не начинающиеся с приглашений являются выводом интерпретатора. Обратите внимание, что строка, в которой содержится лишь вспомогательное приглашение ("... ") означает, что вам нужно ввести пустую строку — этот способ используется для завершения многострочных команд.

Большинство примеров в этом руководстве — даже те, которые вводятся в интерактивном режиме — содержат комментарии. Комментарии в Python начинаются с символа решетки # (hash) — и продолжаются до физического конца строки. Комментарии могут находиться как в начале строки, так и следовать за пробельными символами или кодом — но не содержаться внутри строки. Символ решётки в строке остаётся лишь символом решётки. Поскольку комментарии предназначены для того, чтобы сделать код более понятным, и не интерпретируются Python - при вводе примеров они могут быть опущены.

Несколько примеров:

# это первый комментарий
SPAM = 1                 # а это второй комментарий
                         # ... и наконец третий!
STRING = "# Это не комментарий."

Использование Python в качестве калькулятора

[править]

Давайте опробуем несколько простых команд Python. Запустите интерпретатор и дождитесь появления основного приглашения — >>>. (Это не должно занять много времени.)

Числа

[править]

Поведение интерпретатора сходно поведению калькулятора: вы вводите выражение, а в ответ он выводит значение. Синтаксис выражений прямолинеен: операторы +, -, * и / работают также как и в большинстве других языков (например, Паскале или C); для группировки можно использовать скобки. Например:

>>> 2+2
4
>>> # Это комментарий
... 2+2
4
>>> 2+2  # а вот комментарий на одной строке с кодом
4
>>> (50-5*6)/4
5
>>> # Деление целых чисел возвращает округленное к минимальному значение:
... 7//3
2
>>> 7//-3
-3

Знак равенства ('=') используется для присваивания переменной какого-либо значения. После этого действия в интерактивном режиме ничего не выводится:

>>> width = 20
>>> height = 5*9
>>> width * height
900

Значение может быть присвоено нескольким переменным одновременно:

>>> x = y = z = 0  # Нулевые x, y и z
>>> x
0
>>> y
0
>>> z
0

Переменные должны быть определены (defined) (должны иметь присвоенное значение) перед использованием, иначе будет сгенерирована ошибка:

 >>> # попытка получить доступ к неопределённой переменной
... n
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'n' is not defined

Присутствует полная поддержка операций с плавающей точкой; операторы с операндами смешанного типа конвертируют целочисленный операнд в число с плавающей точкой:

>>> 3 * 3.75 / 1.5
7.5
>>> 7.0 / 2
3.5

Также поддерживаются комплексные числа; к мнимым частям добавляется суффикс j или J. Комплексные числа с ненулевым вещественным компонентом записываются в виде (<вещественная_часть>+<мнимая_часть>j), или могут быть созданы с помощью функции complex(<вещественная_часть>, <мнимая_часть>).

>>> 1j * 1J
(-1+0j)
>>> 1j * complex(0, 1)
(-1+0j)
>>> 3+1j*3
(3+3j)
>>> (3+1j)*3
(9+3j)
>>> (1+2j)/(1+1j)
(1.5+0.5j)

Комплексные числа всегда представлены в виде двух чисел с плавающей точкой - вещественной и мнимой частями. Для получения этих частей из комплексного числа z используйте z.real и z.imag.

>>> a=1.5+0.5j
>>> a.real
1.5
>>> a.imag
0.5

Функции конвертации (приведения) к вещественным и целым числам (float(), int() и long()) не работают с комплексными числами — нет единственно правильного способа сконвертировать комплексное число в вещественное. Используйте функцию abs(z) чтобы получить модуль числа (в виде числа с плавающей точкой) или z.real чтобы получить его вещественную часть:

>>> a=3.0+4.0j
>>> float(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: can`t convert complex to float; use abs(z)
>>> a.real
3.0
>>> a.imag
4.0
>>> abs(a)  # sqrt(a.real**2 + a.imag**2)
5.0
>>>

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

>>> tax = 12.5 / 100
>>> price = 100.50
>>> price * tax
12.5625
>>> price + _
113.0625
>>> round(_, 2)
113.06
>>>

Эта переменная для пользователя должна иметь статус только для чтения. Не навязывайте ей значение собственноручно — вы создадите независимую переменную с таким же именем, скрывающую встроенную переменную вместе с её магическими свойствами.

Строки

[править]

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

>>> 'spam eggs'
'spam eggs'
>>> 'doesn\'t'
"doesn't"
>>> "doesn't"
"doesn't"
>>> '"Yes," he said.'
'"Yes," he said.'
>>> "\"Yes,\" he said."
'"Yes," he said.'
>>> '"Isn\'t," she said.'
'"Isn\'t," she said.'

Строковые литералы могут быть разнесены на несколько строк различными способами. Могут быть использованы продолжающие строки, с обратным слэшем в качестве последнего символа строки, сообщающим о том, что следующая строка есть продолжение текущей[8]:

hello = "This is a rather long string containing\n\
several lines of text just as you would do in C.\n\
    Note that whitespace at the beginning of the line is\
 significant."

print (hello)

Обратите внимание, что для переноса строки все ещё нужно указывать \n; строка, за которой следует обратный слэш перенесена не будет. Запуск примера выведет следующее:

This is a rather long string containing
several lines of text just as you would do in C.
    Note that whitespace at the beginning of the line is significant.

Если мы объявим строковой литерал сырым (raw)[9] — символы \n не будут конвертированы в новые строки, таким образом и обратный слэш в конце строки, и символ новой строки в исходном коде — будут добавлены в строку в виде данных. Следовательно, код из примера:

hello = r"This is a rather long string containing\n\
several lines of text much as you would do in C."

print (hello)

выведет:

This is a rather long string containing\n\

several lines of text much as you would do in C.

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

print """
Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to
"""

выводит в результате следующее:

Usage: thingy [OPTIONS]
     -h                        Display this usage message
     -H hostname               Hostname to connect to

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

Строки могут конкатенироваться (склеиваться вместе) оператором + и быть повторенными оператором *:

>>> word = 'Help' + 'A'
>>> word
'HelpA'
>>> '<' + word*5 + '>'
'<HelpAHelpAHelpAHelpAHelpA>'

Два строковых литерала, расположенные друг за другом, автоматически конкатенируются; первая строка в предыдущем примере также могла быть записана как word = 'Help' 'A'; это работает только с двумя литералами — не с произвольными выражениями, содержащими строки.

>>> 'str' 'ing'                   #  <-  Так — верно
'string'
>>> 'str'.strip() + 'ing'   #  <-  Так — верно
'string'
>>> 'str'.strip() 'ing'     #  <-  Так — не верно
  File "<stdin>", line 1, in ?
    'str'.strip() 'ing'
                      ^
SyntaxError: invalid syntax

Строки могут быть проиндексированы; также как и в C, первый символ строки имеет индекс 0. Отсутствует отдельный тип для символов; символ является строкой с единичной длиной. Подобно языку Icon, подстроки могут определены через нотацию срезов (slice): два индекса, разделенных двоеточием.

>>> word = 'Help' + 'A'
>>> word[4]
'A'
>>> word[0:2]
'He'
>>> word[2:4]
'lp'

Индексы срезов имеют полезные значения по умолчанию; опущенный первый индекс заменяется нулём, опущенный второй индекс подменяется размером срезаемой строки.

>>> word[:2]    # Первые два символа
'He'
>>> word[2:]    # Всё, исключая первые два символа
'lpA'

В отличие от строк в C, строки Python не могут быть изменены. Присваивание по позиции индекса строки вызывает ошибку:

>>> word[0] = 'x'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: object doesn't support item assignment
>>> word[:1] = 'Splat'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: object doesn't support slice assignment

Тем не менее, создание новой строки со смешанным содержимым — процесс легкий и очевидный:

>>> 'x' + word[1:]
'xelpA'
>>> 'Splat' + word[4]
'SplatA'

Полезный инвариант операции среза: s[:i] + s[i:] эквивалентно s.

>>> word[:2] + word[2:]
'HelpA'
>>> word[:3] + word[3:]
'HelpA'

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

>>> word[1:100]
'elpA'
>>> word[10:]
''
>>> word[2:1]
''

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

>>> word[-1]     # Последний символ
'A'
>>> word[-2]     # Предпоследний символ
'p'
>>> word[-2:]    # Последние два символа
'pA'
>>> word[:-2]    # Всё, кроме последних двух символов
'Hel'

Но обратите внимание, что -0 действительно эквивалентен 0 - это не отсчет справа.

>>> word[-0]     # (поскольку -0 равен 0)
'H'

Отрицательные индексы вне диапазона обрезаются, но не советуем делать это с одно-элементными индексами (не-срезами):

>>> word[-100:]
'HelpA'
>>> word[-10]    # ошибка
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
IndexError: string index out of range

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

 +---+---+---+---+---+
 | H | e | l | p | A |
 +---+---+---+---+---+
 0   1   2   3   4   5
-5  -4  -3  -2  -1

Первый ряд чисел дает позицию индексов строки от 0 до 5; второй ряд описывает соответствующие отрицательные индексы. Срез от i до j состоит из всех символов между правым и левым краями, отмеченными, соответственно, через i и j.

Для всех не-негативных индексов длина среза — разность между индексами, при условии что оба индекса находятся в диапазоне. Например, длина word[1:3] — 2.

Встроенная функция len() возвращает длину строки[10]:

>>> s = 'supercalifragilisticexpialidocious'
>>> len(s)
34
Смотрите также:
Перечисляемые типы
Строки, а также строки в Unicode, которые описаны в следующем разделе — примеры перечисляемых типов и поддерживают привычные для этих типов операции.
Строковые методы
И обычные строки, и строки в Unicode поддерживают большое количество методов для поиска и простых трансформаций.
Форматирование строк
Здесь описано форматирование строк с применением функции str.format()
Операции форматирования строк
Операции форматирования, вызывающиеся тогда, когда обычные строки или строки в Unicode оказываются левым операндом относительно оператора %, более детально рассмотрены здесь.

Строки Unicode

[править]

Начиная с Python версии 2.0 программисту стал доступен новый тип данных для хранения текста: объект Unicode. Он может быть использован для хранения и управления данными в Unicode (см. http://www.unicode.org/) и хорошо интегрируется с существующими строковыми объектами, обеспечивая автоматическую конверсию в тех случаях, когда это необходимо.

Преимущество набора Unicode состоит в том, что он предоставляет порядковый номер для любого символа из любой письменности, использовавшейся в современных или древнейших текстах. До этих пор, для символов в сценарии было доступно лишь 256 номеров. Тексты обычно привязывались к кодовой странице, которая устанавливала в соответствие порядковые номера и символы сценария. Это приводило к серьезным путаницам, особенно в том, что касалось интернационализации[11] программного продукта. Unicode решает эти проблемы определяя единую кодовую страницу для всех письменностей.

Создание в Python строк в Unicode является действием настолько же простым, насколько простым является и создание обычных строк:

>>> u'Hello World !'
u'Hello World !'

Строчная буква "u" перед кавычкой указывает на необходимость создания Unicode-строки. Если вы хотите включить в строку специальные символы — вы можете сделать это используя кодирование Unicode-Экранированием Python (Python Unicode-Escape encoding). Следующий пример поясняет как:

>>> u'Hello\u0020World !'
u'Hello World !'

Экранированная последовательность \u0020 указывает на позицию, куда требуется вставить символ Unicode с порядковым номером 0x0020 (символ пробела).

Другие символы интерпретируются с использованием соответствующих им порядковых значений тем же способом, что и порядковые номера Unicode. Если у вас есть буквенные строки в стандартной кодировке Latin-1, использующейся во многих западных странах — вы найдёте удобным то, что первые 256 символов кодировки Unicode полностью совпадают с 256 символами кодировки Latin-1.

Для экспертов присутствует также сырой режим — такой же, как и для обычных строк. Вам требуется поставить перед открывающей кавычкой префикс "ur", чтобы Python использовал кодирование Сырым-Unicode-Экранированием (Raw-Unicode-Escape encoding). Описанная выше конверсия \uXXXX будет применена только при условии наличия перед строчной "u" нечётного количества обратных слэшей.

>>> ur'Hello\u0020World !'
u'Hello World !'
>>> ur'Hello\\u0020World !'
u'Hello\\\\u0020World !'

Сырой режим наиболее полезен, если вам необходимо ввести множество обратных слэшей — это может потребоваться в случае использования регулярных выражений.

Помимо стандартных способов кодирования, Python предоставляет целый набор различных способов создания Unicode-строк основываясь на известной кодировке.

Встроенная функция unicode() предоставляет доступ ко всем зарегистрированным кодекам (кодировщикам и декодировщикам) Unicode. Наиболее известными кодировками, которые могут использовать эти кодеки являются Latin-1, ASCII, UTF-8 и UTF-16. Последние две — кодировки с различными длинами элементов — они могут хранить каждый символ Unicode в одном или более байтах[12]. Кодировкой по умолчанию установлена ASCII, принимающая все символы в диапазоне от 0 до 127 и отвергающая любые другие символы сообщением об ошибке. В случае, если Unicode-строка выводится на печать, пишется в файл или конвертируется функцией str() — конвертирование производится с использованием кодировки по умолчанию.

>>> u"abc"
u'abc'
>>> str(u"abc")
'abc'
>>> u"äöü"
u'\xe4\xf6\xfc'
>>> str(u"äöü")
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)

Для конвертирования Unicode-строки в строку с использованием желаемой кодировки, объекты Unicode предоставляют метод encode(), имеющей формат encode([encoding,[,errors]]. Encoding - необязательный аргумент, указывающий в какую кодировку стоит перевести строку. По умолчанию - ASCII. Errors - так же необязательный аргумент - указывает на способ обработки ошибок кодировщиком. По умолчанию используется 'strict' - если символ не может быть представлен в данной кодировке, генерируется исключение UnicodeError. Другие возможные значения - 'ignore' (отсутствующие символы удаляются) и 'replace' - (отсутствующие в кодировке символы заменяются, обычно на символ '?'). Для именования кодировок предпочитаются названия записанные в нижнем регистре.

>>> u"äöü".encode('utf-8')
'\xc3\xa4\xc3\xb6\xc3\xbc'

Если у вас есть в наличии данные в некоторой кодировке, а вы хотите создать на их основе соответствующую строку в Unicode — вы можете использовать функцию unicode(), передав название кодировки вторым аргументом.

>>> unicode('\xc3\xa4\xc3\xb6\xc3\xbc', 'utf-8')
u'\xe4\xf6\xfc'

Списки

[править]

В языке Python доступно некоторое количество составных типов данных, использующихся для группировки прочих значений вместе. Наиболее гибкий из них — список (list); его можно выразить через разделённые запятыми значения (элементы), заключённые в квадратные скобки. Элементы списка могут быть разных типов.

>>> a = ['spam', 'eggs', 100, 1234]
>>> a
['spam', 'eggs', 100, 1234]

Подобно индексам в строках, индексы списков начинаются с нуля, списки могут быть срезаны, объединены (конкатенированы) и так далее:

>>> a[0]
'spam'
>>> a[3]
1234
>>> a[-2]
100
>>> a[1:-1]
['eggs', 100]
>>> a[:2] + ['bacon', 2*2]
['spam', 'eggs', 'bacon', 4]
>>> 3*a[:3] + ['Boo!']
['spam', 'eggs', 100, 'spam', 'eggs', 100, 'spam', 'eggs', 100, 'Boo!']

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

>>> a
['spam', 'eggs', 100, 1234]
>>> a[2] = a[2] + 23
>>> a
['spam', 'eggs', 123, 1234]

Присваивание срезу также возможно, и это действие может даже изменить размер списка или полностью его очистить:

>>> # Заменим некоторые элементы:
... a[0:2] = [1, 12]
>>> a
[1, 12, 123, 1234]
>>> # Удалим немного:
... a[0:2] = []
>>> a
[123, 1234]
>>> # Вставим пару:
... a[1:1] = ['bletch', 'xyzzy']
>>> a
[123, 'bletch', 'xyzzy', 1234]
>>> # Вставим (копию) самого себя в начало
>>> a[:0] = a
>>> a
[123, 'bletch', 'xyzzy', 1234, 123, 'bletch', 'xyzzy', 1234]
>>> # Очистка списка: замена всех значений пустым списком
>>> a[:] = []
>>> a
[]

Встроенная функция len() также применима к спискам:

>>> a = ['a', 'b', 'c', 'd']
>>> len(a)
4

Вы можете встраивать списки (создавать списки, содержащие другие списки), например так:

>>> q = [2, 3]
>>> p = [1, q, 4]
>>> len(p)
3
>>> p[1]
[2, 3]
>>> p[1][0]
2

Вы можете добавить что-нибудь в конец списка.

>>> p[1].append('xtra')     # Смотрите раздел Подробнее о списках
>>> p
[1, [2, 3, 'xtra'], 4]
>>> q
[2, 3, 'xtra']

Обратите внимание, что в последнем примере p[1] и q на самом деле ссылаются на один и тот же объект! Мы вернёмся к семантике объектов позже.

Первые шаги к программированию

[править]

Безусловно, Python можно использовать для более сложных задач, чем сложение двух чисел. Например, мы можем вывести начальную последовательность чисел Фибоначчи таким образом:

>>> # Ряд Фибоначчи:
... # сумма двух элементов определяет следущий элемент
... a, b = 0, 1
>>> while b < 10:
...       print (b)
...       a, b = b, a+b
... 
1
1
2
3
5
8

Этот пример представляет нам некоторые новые возможности.

  • Первая строка содержит множественное присваивание (multiple assignment): переменные a и b параллельно получают новые значения — 0 и 1. В последней строке этот метод используется снова, демонстрируя тот факт, что выражения по правую сторону [от оператора присваивания] всегда выполняются первыми — перед тем, как какое-либо присваивание будет иметь место. Сами же правосторонние выражения вычисляются слева направо.
  • Цикл while исполняется до тех пор, пока условие (здесь: b < 10) остается истиной. В Python, также как и в C, любое ненулевое значение является истиной (true); ноль является ложью (false). Условием может быть строка или значение списка, вообще любая последовательность; все, что имеет не-нулевую длину является истиной, пустые последовательности являются ложью. Проверка, использованная в примере — простое условие. Стандартные операторы сравнения записываются также как и в C: < (меньше чем), > (больше чем), == (равно, идентично), <= (меньше или равно), >= (больше или равно) и != (не равно).
  • Тело цикла описано с использованием отступа (indented): Отступы — это способ группировки выражений в Python. Python (пока!) не имеет какого-либо разумного и удобного средства для редактирования строк ввода, поэтому вам нужно использовать табуляцию или пробел(ы) для отступа в каждой строке. На практике вы будете подготавливать более сложный ввод для Python в текстовом редакторе, а большинство из них имеют сервис авто-отступа. По окончанию ввода составного выражения в интерактивном режиме, необходимо закончить его пустой строкой — признаком завершения (поскольку парсер не может угадать, когда вами была введена последняя строка). Обратите внимание, что размер отступа в каждой строке основного блока должен быть одним и тем же[13].
  • Оператор print выводит значения переданного(-ных) ему выражения(-ний). Это поведение отличается от обычного вывода выражения (как происходило выше в примерах с калькулятором) тем, каким способом обрабатываются ряды выражений и строки. Строки выводятся без кавычек и между элементами вставляются пробелы, благодаря чему форматирование вывода становится более удобным — как, например, здесь:
>>> i = 256*256
>>> print ('Значением i является', i)
Значением i является 65536
Замыкающая строку запятая отменяет перевод строки после вывода:
>>> a, b = 0, 1
>>> while b < 1000:
...     print b,
...     a, b = b, a+b
... 
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
Обратите внимание на то, что интерпретатор вставляет новую строку перед тем как выводит следующее приглашение, если последняя строка не была завершена.

Дополнительные средства управления порядком выполнения[14]

[править]

Помимо описанного выше оператора while, в Python доступны привычные операторы управления из других языков, но с некоторыми различиями.

Оператор if

[править]

Возможно наиболее широко известный тип выражения &#151; оператор if. Пример:

>>> x = int(raw_input("Введите, пожалуйста, целое число: "))
print(x)
>>> if x < 0:
...      x = 0
...      print ('Отрицательное значение изменено на ноль')
... elif x == 0:
...      print ('Ноль')
... elif x == 1:
...      print ('Единица')
... else:
...      print ('Больше')
...
Больше

Блоков elif может быть ноль или больше, а блок else не обязателен. Ключевое слово „elif“ — краткая запись „else if“ — позволяет избавиться от чрезмерных отступов. Последовательность if ... elif ... elif ... — замена операторам switch или case, которые можно встретить в других языках.

Оператор for

[править]

Оператор for в Python немного отличается от того, какой вы, возможно, использовали в C или Pascal. Вместо неизменного прохождения по арифметической прогрессии из чисел (как в Pascal) или предоставления пользователю возможности указать шаг итерации и условие останова (как в С), оператор for в Python проходит по всем элементам любой последовательности (списка или строки) в том порядке, в котором они в ней располагаются. Например (игра слов не подразумевалась):[15]

>>> # Измерим несколько строк:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
...     print x, len(x)
...
cat 3
window 6
defenestrate 12

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

>>> for x in a[:]: # создать срез-копию всего списка
...    if len(x) > 6: a.insert(0, x)
...
>>> a
['defenestrate', 'cat', 'window', 'defenestrate']

Функция range()

[править]

Если вам нужно перебрать последовательность чисел, встроенная функция range() придёт на помощь. Она генерирует списки, содержащие арифметические прогрессии:

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Указанная точка завершения никогда не оказывается частью сгенерированного списка; вызов range(10) генерирует список из десяти значений — потенциальные индексы для элементов последовательности длиной 10. Позволено установить начало списка на другое число или указать другую величину инкремента (даже негативную; иногда её называют шагом (step)).

>>> range(5, 10)
[5, 6, 7, 8, 9]
>>> range(0, 10, 3)
[0, 3, 6, 9]
>>> range(-10, -100, -30)
[-10, -40, -70]

Чтобы пройти по всем индексам какой-либо последовательности, совместите вызовы range() и len() следующим образом:

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print i, a[i]
... 
0 Mary
1 had
2 a
3 little
4 lamb

Однако в большинстве таких случаев удобно использовать функцию enumerate(). Обратитесь к техникам проходов циклом.

Операторы break и continue, а также условие else в циклах

[править]

Оператор break, как и в C, прерывает выполнение ближайшего заключающего цикла for или while.

Оператор continue, также заимствованный из C, продолжает выполнение цикла со следующей итерации.

Операторы циклов могут иметь ветвь else; она исполняется, когда цикл прекращает сквозной анализ списка (в случае for) или когда условие становится ложным (в случае while), но не в тех случаях, когда цикл прерывается по break. Это поведение иллюстрируется следующим примером, в котором производится поиск простых чисел:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'равно', x, '*', n//x
...             break
...     else:
...         # циклу не удалось найти множитель
...         print n, '- простое число'
...
2 - простое число
3 - простое число
4 равно 2 * 2
5 - простое число
6 равно 2 * 3
7 - простое число
8 равно 2 * 4
9 равно 3 * 3

Оператор pass

[править]

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

>>> while True:
...       pass # Ожидание прерывания c клавиатуры (Ctrl+C) в режиме занятости
...

Этот оператор часто используется для создания минималистичных классов, к примеру исключений (exceptions), или для игнорирования нежелательных исключений:

>>> class ParserError(Exception):
...     pass
...
>>> try:
...     import audioop
... except ImportError:
...     pass
...

Другой вариант: pass может применяться в качестве заглушки для тела функции или условия при создании нового кода, позволяя вам сохранить абстрактный взгляд на вещи. С другой стороны, оператор pass игнорируется без каких-либо сигналов, и лучшим выбором было бы породить исключение NotImplementedError:

>>> def initlog(*args):
...     raise NotImplementedError   # Открыть файл для логгинга, если он ещё не открыт
...     if not logfp:
...         raise NotImplementedError  # Настроить заглушку для логгинга
...     raise NotImplementedError('Обработчик инициализации лога вызовов')
...

Если бы здесь использовались операторы pass, а позже вы бы запускали тесты, они могли бы упасть без указания причины. Использование NotImplementedError принуждает этот код породить исключение, сообщая вам конкретное место, где присутствует незавершённый код. Обратите внимание на два способа порождения исключений. Первый способ — без сообщения, но сопровождаемый комментарием, позволяет вам оставить комментарий, когда вы будете подменять выброс исключения рабочим кодом, который, в свою очередь (в идеале) будет хорошим описанием блока кода, для которого исключение предназначалось заглушкой. Однако передача сообщения вместе с исключением, как в третьем примере, обуславливает более насыщенный информацией вывод при отслеживании ошибки.

Определение функций

[править]

Мы можем создать функцию, которая выводит числа Фибоначчи до некоторого предела:

>>> def fib(n):    # вывести числа Фибоначчи меньшие (вплоть до) n
...     """Выводит ряд Фибоначчи, ограниченный n."""
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
...
>>> # Теперь вызовем определенную нами функцию:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

Зарезервированное слово def предваряет определение функции. За ним должны следовать имя функции и заключённый в скобки список формальных параметров. Выражения, формирующие тело функции, начинаются со следующей строки и должны иметь отступ.

Первым выражением в теле функции может быть строковой литерал — этот литерал является строкой документации функции, или док-строкой (docstring). (Больше информации о док-строках вы найдёте в разделе Строки документации) Существуют инструменты, которые используют док-строки для того, чтобы сгенерировать печатную или онлайн-документацию или чтобы позволить пользователю перемещаться по коду интерактивно; добавление строк документации в ваш код — это хорошая практика, постарайтесь к ней привыкнуть.

Исполнение функции приводит к созданию новой таблицы символов[16], использующейся для хранения локальных переменных функции. Если быть более точными, все присваивания переменных в функции сохраняют значение в локальной таблице символов; при обнаружении ссылки на переменную, в первую очередь просматривается локальная таблица символов, затем локальная таблица символов для окружающих функций, затем глобальная таблица символов и, наконец, таблица встроенных имён. Таким образом, глобальным переменным невозможно прямо присвоить значения внутри функций (до тех пор, пока они поименованы в глобальном выражении) несмотря на то, что ссылки на них могут использоваться.

Фактические параметры (аргументы) при вызове функции помещаются в локальную таблицу символов вызванной функции; в результате аргументы передаются через вызов по значению (call by value) (где значение — это всегда ссылка (reference) на объект, а не значение его самого)[17]. Если одна функция вызывает другую — то для этого вызова создается новая локальная таблица символов.

При определении функции её имя также помещается в текущую таблицу символов. Тип значения, связанного с именем функции, распознается интерпретатором как функция, определённая пользователем (user-defined function). Само значение может быть присвоено другому имени, которое затем может также использоваться в качестве функции. Эта система работает в виде основного механизма переименования:

>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

Если вы использовали в работе другие языки программирования, вы можете возразить, что fib — это не функция, а процедура, поскольку не возвращает никакого значения. На самом деле, даже функции без ключевого слова return возвращают значение, хотя и более скучное. Такое значение именуется None (это встроенное имя). Описание значения None обычно придерживается за интерпретатором, если оно оказывается единственным значением, которое нужно вывести. Вы можете проследить за этим процессом, если действительно хотите, используя оператор print:

>>> fib(0)
>>> print fib(0)
None

Довольно легко написать функцию, которая возвращает список чисел из ряда Фибоначчи, вместо того, чтобы выводить их:

>>> def fib2(n): # вернуть числа Фибоначчи меньшие (вплоть до) n
...     """Возвращает список чисел ряда Фибоначчи, ограниченный n."""
...     result = []
...     a, b = 0, 1
...     while b < n:
...         result.append(b)    # см. ниже
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # вызываем
>>> f100                # выводим результат
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Этот пример, как уже привычно, демонстрирует некоторые новые возможности Python:

  • Оператор return завершает выполнение функции, возвращая некоторое значение. Оператор return без аргумента возвращает None. Достижение конца функции также возвращает None.
  • Выражение result.append(b) вызывает метод объекта списка result. Метод — это функция, которая "принадлежит" (belongs) объекту и указывается через выражение obj.methodname, где obj — некоторый объект (может быть выражением), а methodname — имя метода, который определён типом объекта. Различные типы определяют различные методы. Методы разных типов могут иметь одинаковое имя, не оказываясь причинами неопределённостей. (Возможно определять ваши собственные типы объектов и методы используя классы, что будет рассмотрено позже в этом учебнике.) Метод append(), показанный в примере, определён для объектов типа список — он добавляет в конец списка новый элемент. В данном примере это действие эквивалентно выражению result = result + [b], но более эффективно.

Подробнее об определении функций

[править]

Также есть возможность определять функции с переменным количеством аргументов. Для этого существует три формы, которые также можно использовать совместно.

Значения аргументов по умолчанию

[править]

Наиболее полезная форма — указать значение по умолчанию для одного или более аргументов. Таким образом создаётся функция, которая может быть вызвана с меньшим количеством аргументов, чем в её определении, и корректно их принять. Например[18]:

def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('y', 'ye', 'yes'): return True
        if ok in ('n', 'no', 'nop', 'nope'): return False
        retries = retries - 1
        if retries < 0: raise IOError, 'refusenik user'
        print complaint

Эта функция может быть вызвана, например, так: ask_ok('Do you really want to quit?') или так: ask_ok('OK to overwrite the file?', 2).

Этот пример также знакомит вас с зарезервированным словом in. Посредством его можно проверить, содержит или нет последовательность определённое значение.

Значения по умолчанию вычисляются в точке определения функции — в определяющей области, поэтому код

i = 5

def f(arg=i):
    print arg

i = 6
f()

выведет 5.

Важное предупреждение: Значение по умолчанию вычисляется лишь единожды. Это имеет вес, когда значением по умолчанию является изменяемый объект, такой как список, словарь (dictionary) или экземляры большинства классов. Например, следующая функция накапливает переданные ей аргументы с последовательными вызовами:

def f(a, L=[]):
    L.append(a)
    return L

print f(1)
print f(2)
print f(3)

Она выведет

[1]
[1, 2]
[1, 2, 3]

Именованные аргументы [19]

[править]

Функции также могут быть вызваны с использованием именованных аргументов (keyword arguments) в форме "имя = значение". Например, нижеприведённая функция[20]:

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"

могла бы быть вызвана любым из следующих способов[21]:

parrot(1000)
parrot(action = 'VOOOOOM', voltage = 1000000)
parrot('a thousand', state = 'pushing up the daisies')
parrot('a million', 'bereft of life', 'jump')

а эти случаи оказались бы неверными[22]:

parrot()                     # пропущен требуемый аргумент
parrot(voltage=5.0, 'dead')  # неименованный аргумент следом за именованным
parrot(110, voltage=220)     # повторное значение аргумента
parrot(actor='John Cena')  # неизвестное имя аргумента

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

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

Если в определении функции присутствует завершающий параметр в виде **имя — он принимает словарь (подробнее в разделе Справочника — Маппинг типов — словари), содержащий все именованные аргументы, исключая те, которые соответствуют формальным параметрам. Можно совместить эту особенность с поддержкой формального параметра в формате *имя (описывается в следующем подразделе), который получает кортеж (tuple), содержащий все позиционные аргументы, следующие за списком формальных параметров. (параметр в формате *имя должен описываться перед параметром в формате **имя.) Например, если мы определим такую функцию[24]:

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, "?"
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print "-"*40
    keys = keywords.keys()
    keys.sort()
    for kw in keys: print kw, ":", keywords[kw]

то её можно будет вызвать так[25]:

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

и она, конечно же, выведет[26]:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

Обратите внимание, что метод sort() списка имён (ключей) поименованных аргументов (keys) вызывается перед выводом содержимого словаря keywords; если бы этого не было сделано, порядок вывода аргументов был бы неопределён.

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

[править]

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

def write_multiple_items(file, separator, *args):
    file.write(separator.join(args))

Распаковка списков аргументов

[править]

Обратная ситуация возникает когда аргументы уже содержатся в списке или в кортеже, но должны быть распакованы для вызова функции, требующей отдельных позиционных аргументов. Например, встроенная функция range() ожидает отдельных аргументов start и stop соответственно. Если они не доступны раздельно, для распаковки аргументов из списка или кортежа в вызове функции используйте *-оператор:

>>> range(3, 6)             # обычный вызов с отдельными аргументами
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args)            # вызов с распакованными из списка аргументами
[3, 4, 5]

Схожим способом, словари могут получать именованные аргументы через **-оператор[27]:

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it.",
...     print "It's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. It's bleedin' demised !

Модель lambda

[править]

По частым просьбам, в Python были добавлены несколько возможностей, которые были привычны для функциональных языков программирования, таких как Lisp. Используя зарезервированное слово lambda вы можете создать небольшую анонимную функцию. Здесь представлена функция, которая возвращает сумму двух её аргументов: lambda a, b: a+b. Формы lambda могут быть использованы в любом месте где требуется объект функции. При этом они синтаксически ограничены на одно выражение. Семантически, они лишь синтаксический сахар для обычного определения функции. Как и определения вложенных функций, lambda-формы могут ссылаться на переменные из содержащей их области видимости:

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43

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

[править]

Перечислим некоторые существующие соглашения по содержимому строк документации и их форматированию.

По поводу форматирования строк документации и их содержимого постоянно появляются всё новые соглашения.

Первая строка всегда должна быть сжатой, лаконичной сводкой о назначении объекта. Для краткости, в ней не обязательно присутствие имени типа или объекта, поскольку они доступны другими способами (исключая случай, когда имя функции оказывается глаголом, описывающим суть операции). Эта строка должна начинаться с прописной буквы и оканчиваться точкой.

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

Парсер Python не обрабатывает отступы в много-строковых литералах, поэтому инструментам, которые работают над документацией, предлагается, по желанию, делать это самим. Производится это по следующему соглашению. Первая непустая строка после первой строки литерала определяет величину отступа всего литерала документации. (Мы не можем использовать первую строку, поскольку она обычно выравнивается по открывающим кавычкам и её отступ в литерале не явен). Пробельный „эквивалент“ этого отступа затем отрезается от начала всех строк литерала. Строк с меньшим отступом не должно обнаруживаться, но если они встретились, весь их начальный отступ должен быть обрезан. Эквивалентность пробельных замен может быть протестирована развертыванием табуляции (обычно, к 8 пробелам).

Вот пример многострочной документации (док-строки):

>>> def my_function():
...     """Не делаем ничего, но документируем.
...
...     Нет, правда, эта функция ничего не делает.
...     """
...     pass
...
>>> print my_function.__doc__
Не делаем ничего, но документируем.

    Нет, правда, эта функция ничего не делает.

Интермеццо: Стиль написания кода

[править]

Теперь, когда вам предстоит писать более объёмные и сложные блоки кода на Python, настало время поговорить о стиле написания кода ( coding style). Код на большинстве языков программирования может быть записан (или, точнее говоря, отформатирован ( formatted)) различными способами; некоторые из них более читабельны, некоторые — нет. Стремление к написанию лёгкого для прочтения другими кода всегда считалось хорошим тоном, и выбор правильного стиля для кода крайне ему способствует.

В случае языка Python, в качестве руководства по стилю было создано предложение PEP8[28], которого придерживаются создатели большинства проектов. В нём учреждается чрезвычайно читабельный и приятный для глаза стиль написания кода. В некоторый момент с ним должен ознакомиться каждый разработчик на Python. Приведём здесь избранные, наиболее важные, пункты:

  • Используйте отступ в 4 пробела, не используйте табуляцию
4 пробела легко опознаются и в случае небольших отступов (хватает места для глубоких вложений) и в случае больших отступов (приятнее читается). Табуляция вносит путаницу и лучше от неё воздержаться.
  • Разделяйте строки так, чтобы их длина не превышала 79-и символов
Это поможет пользователям с небольшими экранами, а пользователям с большими экранами позволит уложить несколько файлов с исходным кодом рядом.
  • Используйте пустые строки для отделения функций, классов, и крупных блоков внутри функций.
  • При возможности располагайте комментарий на отдельной строке.
  • Используйте строки документации (док-строки)
  • Применяйте пробелы вокруг операторов и после запятых, но не добавляйте их в конструкции со скобками:
spam(ham[1], {eggs: 2}, 3, 4) #правильно
spam( ham[ 1 ], { eggs: 2 }, 3 , 4 ) #неправильно
  • Называйте ваши классы и функции единообразно; соглашение следующее: используйте CamelCase[29] для именования классов и нижний_регистр_с_подчёркиваниями[30] для функций и методов. (обращайтесь к разделу Первый взгляд на классы за дополнительной информации о классах и методах)
  • Не используйте в вашем коде изощрённых кодировок[31], если он рассчитан на использование в интернациональной среде. Стандартный набор ASCII всегда работает на ура[32].

Структуры данных

[править]

Эта глава описывает подробнее некоторые вещи, которые вы уже изучили, а также раскрывает некоторые новые темы.

Подробнее о списках

[править]

У типа данных список также имеются не описанные ранее методы. Ниже описаны все методы объектов списка:

list.append(x)
Добавить элемент к концу списка; эквивалент a[len(a):] = [x]
list.extend(L)
Расширить список за счёт добавления всех элементов переданного списка; эквивалентно a[len(a):] = L.
list.insert(i, x)
Вставить элемент в указанную позицию. Первый аргумент — это индекс того элемента, перед которым требуется выполнить операцию вставки, поэтому вызов a.insert(0, x) вставляет элемент в начало списка, а a.insert(len(a), x) эквивалентно a.append(x).
list.remove(x)
Удалить первый найденный элемент из списка, значение которого — x. Если элемент не найден, генерируется ошибка.
list.pop([i])
Удалить элемент, находящийся на указанной позиции в списке, и возвратить его. Если индекс не указан, a.pop() удаляет и возвращает последний элемент списка. (Квадратные скобки вокруг i в сигнатуре метода означают, что параметр необязателен, а не необходимость набора квадратных скобок в этой позиции. Вы часто будете встречать такую нотацию в Справочнике по библиотеке.)
list.index(x)
Вернуть индекс первого найденного в списке элемента, значение которого равно x. Если элемент не найден, генерируется ошибка.
list.count(x)
Вернуть количество раз, которое x встречается в списке.
list.sort()
Сортировать элементы списка, на месте.
list.reverse()
Обратить порядок элементов списка, на месте.

Пример, использующий большинство методов списка:

>>> a = [66.25, 333, 333, 1, 1234.5]
>>> print a.count(333), a.count(66.25), a.count('x')
2 1 0
>>> a.insert(2, -1)
>>> a.append(333)
>>> a
[66.25, 333, -1, 333, 1, 1234.5, 333]
>>> a.index(333)
1
>>> a.remove(333)
>>> a
[66.25, -1, 333, 1, 1234.5, 333]
>>> a.reverse()
>>> a
[333, 1234.5, 1, 333, -1, 66.25]
>>> a.sort()
>>> a
[-1, 1, 66.25, 333, 333, 1234.5]

Использование списка в качестве стека

[править]

Методы списков позволяют легко использовать список в качестве стека, где последний добавленный элемент становится первым полученным („последним пришел — первым вышел“). Чтобы добавить элемент на вершину стека, используйте метод append(). Чтобы получить элемент с вершины стека, используйте метод pop(), без указания явного индекса. Например:

>>> stack = [3, 4, 5]
>>> stack.append(6)
>>> stack.append(7)
>>> stack
[3, 4, 5, 6, 7]
>>> stack.pop()
7
>>> stack
[3, 4, 5, 6]
>>> stack.pop()
6
>>> stack.pop()
5
>>> stack
[3, 4]

Использование списка в качестве очереди

[править]

Вы можете без труда использовать список также и в качестве очереди, где первый добавленный элемент оказывается первым полученным („первый пришёл — первый вышел“). Чтобы добавить элемент в конец очереди, используйте метод append(). Чтобы получить элемент из начала очереди, используйте метод pop() с нулём в качестве индекса. Например:

>>> queue = ["Eric", "John", "Michael"]
>>> queue.append("Terry")           # Прибыл Terry
>>> queue.append("Graham")          # Прибыл Graham
>>> queue.pop(0)
'Eric'
>>> queue.pop(0)
'John'
>>> queue
['Michael', 'Terry', 'Graham']

Инструменты функционального программирования

[править]

Есть три встроенных функции, которые очень удобно использовать в сочетании со списками: filter(), map() и reduce().

filter(функция, последовательность) возвращает последовательность, состоящую из тех элементов последовательности, для которых функция(элемент) является истиной. Если последовательность — строка (string) или кортеж (tuple), результат будет иметь тот же тип; в других случаях — это всегда список. Например, чтобы найти несколько простых чисел:

>>> def f(x): return x % 2 != 0 and x % 3 != 0 # вернуть истину, если x не делится ни на 2 ни на 3
...
>>> filter(f, range(2, 25)) # отфильтровать диапазон чисел от 2 до 24 через функцию f
[5, 7, 11, 13, 17, 19, 23]

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

>>> def cube(x): return x*x*x # вернуть куб аргумента
...
>>> map(cube, range(1, 11)) # спроецировать функцию cube на диапазон от 1 до 10
[1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]

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

>>> seq = range(8) # seq - числа от 0 до 7
>>> def add(x, y): return x+y # функция add складывает свои аргументы
...
>>> map(add, seq, seq) # пропустить функцию add через две последовательности seq параллельно
[0, 2, 4, 6, 8, 10, 12, 14]

reduce(функция, последовательность) возвращает единственное значение, собранное из результатов вызовов двухаргументной функции с первыми двумя элементами последовательности, затем с полученным результатом и последующим элементом, и так далее. Например, чтобы сосчитать сумму чисел от 1 до 10:

>>> def add(x,y): return x+y # функция add складывает оба аргумента
...
>>> reduce(add, range(1, 11)) # пройти функцией add последовательно аккумулятивно по числам из диапазона от 1 до 10. 
55

Если в последовательности только один элемент — возвращается его значение; если последовательность пуста — порождается исключение.

Можно передать третий аргумент, чтобы обозначить исходное значение. В этом случае исходное значение возвращается для пустой последовательности, функция первоначально применяется к исходному значению и первому элементу последовательности, затем к результату и следующему элементу, и так далее. Пример:

>>> def sum(seq): # определить функцию sum, принимающую последовательность
...     def add(x,y): return x+y # определить внутреннюю функцию add, возвращающую сумму переданных параметров
...     return reduce(add, seq, 0) # вернуть результат аккумулятивного прохождения функцией add по переданной
...                           # последовательности seq с исходным значением 0.
... 
>>> sum(range(1, 11)) # сумма диапазона чисел от 1 до 10
55
>>> sum([]) # сумма пустой последовательности
0

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

Списковые сборки[33]

[править]

Метод списковой сборки обеспечивает легкий способ создать список без обращений к map(), filter() и/или lambda. Определение списка, получающееся в результате, имеет тенденцию читаться проще чем списки, построенные с использованием таких конструкций. Каждая списковая сборка состоит из выражения, за которым следует оператор for, а затем ноль или более операторов for или if. Если в результате вычисления выражения получается кортеж, его нужно обернуть в скобки.

>>> freshfruit = ['  banana', '  loganberry ', 'passion fruit  ']
>>> [weapon.strip() for weapon in freshfruit] # взять каждый элемент списка freshfruit, и временно обозначив его как weapon
...                      # (создав алиас), обрезать у weapon пробелы 
['banana', 'loganberry', 'passion fruit']
>>> vec = [2, 4, 6]
>>> [3*x for x in vec] # умножить каждое число в списке vec на три
[6, 12, 18]
>>> [3*x for x in vec if x > 3] # ...только если число больше трёх
[12, 18]
>>> [3*x for x in vec if x < 2] # ...только если число меньше двух
[]
>>> [[x,x**2] for x in vec] # создать двуэлементный список с числом и его квадратом для каждого элемента vec
[[2, 4], [4, 16], [6, 36]]
>>> [x, x**2 for x in vec] # ошибка - для кортежей необходимы скобки
  File "<stdin>", line 1, in ?
    [x, x**2 for x in vec]
               ^
SyntaxError: invalid syntax
>>> [(x, x**2) for x in vec] # создать двуэлементный кортеж, содержащий число и его квадрат каждого элемента vec
[(2, 4), (4, 16), (6, 36)]
>>> vec1 = [2, 4, 6]
>>> vec2 = [4, 3, -9]
>>> [x*y for x in vec1 for y in vec2] # перемножить каждый элемент вектора vec1 на каждый элемент вектора vec2 последовательно
...                 # (произведение матриц)
[8, 6, -18, 16, 12, -36, 24, 18, -54]
>>> [x+y for x in vec1 for y in vec2] # сложить каждый элемент вектора vec1 с каждым элементом вектора vec2 последовательно
[6, 5, -7, 8, 7, -5, 10, 9, -3]
>>> [vec1[i]*vec2[i] for i in range(len(vec1))] # умножить i-ый элемент списка vec1 на i-ый элемент списка vec2
...                 # для всех i от нуля до длины массива vec1 минус единица включительно
[8, 12, -54]

Списковые сборки являются более гибкими чем функция map() и могут применяться к сложным выражениям и вложенным функциям[34]:

>>> [str(round(355/113.0, i)) for i in range(1,6)] # для всех i от 1 до 5 вернуть результат деления 
...                    # 335 на 113, округлённый до i знаков за запятой
['3.1', '3.14', '3.142', '3.1416', '3.14159']

Вложенные списковые сборки

[править]

Если ваш живот ещё готов воспринять это, списковые сборки могут быть вложенными. Это мощный инструмент, но — как и любой мощный инструмент — их стоит использовать с осторожностью, если и использовать вообще.

Представьте нижеследующий пример матрицы 3x3 в виде списка, содержащего три других списка, по одному в ряд:

>>> mat = [
...        [1, 2, 3],
...        [4, 5, 6],
...        [7, 8, 9],
...       ]

Теперь, если вам нужно подменить строки столбцами, вы можете использовать списковую сборку:

>>> print [[row[i] for row in mat] for i in [0, 1, 2]]
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

При создании вложенных списковых сборках нужно помнить о важной вещи:

Чтобы избежать опасений при конструировании вложенных списковых сборок, читайте справа налево.

Чуть более многословная, но наглядная, версия этого примера позволяет взглянуть на процесс более детально:

for i in [0, 1, 2]:
    for row in mat:
        print row[i],
    print

В реальных случаях лучше предпочесть встроенные функции, чем сложные выражения. В нашем случае поможет функция zip():

>>> zip(*mat)
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

В разделе Распаковка списков аргументов описано предназначение звёздочки в этих строках.

Оператор del

[править]

Существует способ удалить элемент, указывая его индекс, а не его значение: оператор del. В отличие от метода pop(), он не возвращает значения. Оператор del может также использоваться для удаления срезов из списка или полной очистки списка (что мы делали ранее через присваивание пустого списка срезу). Например:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]

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

>>> del a

Ссылка на имя a в дальнейшем вызовет ошибку (по крайней мере до тех пор, пока с ним не будет связано другое значение). Позже мы с вами узнаем другие способы использования del.

Кортежи и последовательности

[править]

Мы видели, что списки и строки поддерживают много привычных свойств, таких как индексирование и операция получения срезов. Существует два подвида типов данных последовательность (sequence) (см. Справочник по библиотеке — Последовательности), и поскольку Python — развивающийся язык, со временем могут быть добавлены другие последовательные типы данных. Итак, существует также и другой, достойный рассмотрения, стандартный последовательный тип данных: кортеж.

Кортеж состоит из некоторого числа значений разделённых запятыми, например:

>>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Кортежи могут быть вложенными:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))

Как видите, кортежи на выводе всегда заключены в скобки, таким образом вложенные кортежи интрепретируются корректно; они могут быть введены и с обрамляющими скобками и без, тем не менее в любом случае скобки чаще всего необходимы (если кортеж — часть более крупного выражения).

Кортежи можно использовать в различных целях. Например: (x, y) пары координат, записи о рабочих из базы данных, и так далее. Кортежи, как и строки, неизменяемы: невозможно присвоить что-либо индивидуальным элементам кортежа (однако, вы можете симулировать большинство схожих эффектов за счёт операций срезов и конкатенации). Также можно создать кортежи, содержащие изменяемые объекты, такие как списки.

Определённая проблема состоит в конструировании кортежей, состоящих из нуля или одного элемента: в синтаксисе языка есть дополнительные хитрости, позволяющие достигнуть этого. Пустые кортежи формируются за счёт пустой пары скобок; кортеж с одним элементом конструируется за счёт запятой, следующей за числом (единственное значение не обязательно заключать в скобки). Необычно, но эффективно. Например:

>>> empty = ()
>>> singleton = 'hello',    # <-- обратите внимание на замыкающую запятую
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

Выражение t = 12345, 54321, 'hello!' является примером упаковки кортежей (tuple packing): значения 12345, 54321 и 'hello!' упаковываются в кортеж вместе. Обратная операция также возможна:

>>> x, y, z = t

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

Здесь есть некоторая асимметрия: упаковка нескольких значений всегда создаёт кортеж, а распаковка работает для любой последовательности.

Множества

[править]

Python также включает тип данных множество. Множество — это неупорядоченная коллекция без дублирующихся элементов. Основные способы использования включают в себя проверку на вхождение и устранение дублирующихся элементов. Объекты множества также поддерживают математические операции, такие как объединение, пересечение, разность (расхождение) и симметричная разность.

Вот небольшая демонстрация[35]:

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> fruit = set(basket)               # создать множество
>>> fruit
set(['orange', 'pear', 'apple', 'banana'])
>>> 'orange' in fruit                 # быстрая проверка вхождения
True
>>> 'crabgrass' in fruit
False
>>> # Демонстрация операций с множеством на буквах из двух слов
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # уникальные буквы в a
set(['a', 'r', 'b', 'c', 'd'])
>>> a - b                              # буквы в a но не в b
set(['r', 'd', 'b'])
>>> a | b                              # все буквы, которые встречаются в a или в b
set(['a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'])
>>> a & b                              # буквы, которые есть и в a и в b
set(['a', 'c'])
>>> a ^ b                              # буквы в a или в b, но не в обоих
set(['r', 'd', 'b', 'm', 'z', 'l'])

Словари

[править]

Другой полезный, встроенный в Python, тип данных — словарь (dictionary) (см. Справочник по библиотеке — Связывающие типы). Словари иногда встречаются в других языках в качестве «ассоциативных записей» или «ассоциативных массивов». В отличие от последовательностей, которые индексируются по диапазону чисел, словари индексируются по ключам (keys), которые, в свою очередь, могут быть любого неизменяемого типа; строки и числа всегда могут быть ключами. Кортежи могут быть ключами только если они составлены из строк, чисел или кортежей; если кортеж содержит какой-либо изменяемый объект, явно или неявно, то он не может быть использован в качестве ключа. Вы не можете использовать списки в роли ключей, поскольку списки могут быть изменены на месте присваиванием по индексу, присваиванием по срезу или такими методами как append() и extend().

Лучше всего воспринимать словарь как неупорядоченный набор пар ключ: значение с требованием, чтобы ключи были уникальны (в пределах одного словаря). Пара скобок создает пустой словарь: {}. Указывая разделённый запятыми список пар ключ: значение внутри скобок, вы поочерёдно добавляете эти пары в словарь; таким же способом словарь пишется в вывод.

Главные операции над словарём — сохранение значения с каким-либо ключом и извлечение значения по указанному ключу. Также возможно удалить пару ключ: значение используя оператор del. Если вы сохраняете значение используя ключ, который уже встречается в словаре — старое значение, ассоциированное с этим ключом, стирается. Извлечение значения по несуществующему ключу вызывает ошибку.

Метод keys() объекта словарь возвращает список всех ключей, использующихся в словаре в случайном порядке (если вы хотите отсортировать его, к списку ключей можно применить метод sort()). Чтобы проверить, содержит ли словарь определённый ключ, используйте ключевое слово in.

Вот небольшой пример использования словарей:

>>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'sape': 4139, 'guido': 4127, 'jack': 4098}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'guido': 4127, 'irv': 4127, 'jack': 4098}
>>> tel.keys()
['guido', 'irv', 'jack']
>>> 'guido' in tel
True

Конструктор dict() строит словарь непосредственно на основе пар ключ-значение, где каждая пара представлена в виде кортежа. Когда пары могут быть сформированы шаблоном, списковые сборки помогут описать список пар ключ-значение более компактно.

>>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'jack': 4098, 'guido': 4127}
>>> dict([(x, x**2) for x in (2, 4, 6)])     # использование списковой сборки
{2: 4, 4: 16, 6: 36}

Позже в учебнике мы изучим выражения-генераторы (Generator Expressions), которые даже лучше подходят для снабжения конструктора dict() парами ключ-значение.

Если ключи являются простыми строками, иногда легче описать пары используя именованные аргументы:

>>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'jack': 4098, 'guido': 4127}

Техника прохода циклом

[править]

При прохождении словарей циклом, ключ и соответствующее ему значение могут быть получены параллельно посредством метода iteritems().

>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.iteritems():
...     print k, v
...
gallahad the pure
robin the brave

При прохождении циклом последовательностей, индекс позиции и соответствующее ему значение могут быть получены одновременно посредством функции enumerate().

>>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print i, v
...
0 tic
1 tac
2 toe

Для того, чтобы пройти циклом две или более последовательности параллельно, вхождения можно сгруппировать функцией zip()[36].

>>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print 'What is your {0}?  It is {1}.'.format(q, a)
...	
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.

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

>>> for i in reversed(range(1,10,2)):
...     print i
...
9
7
5
3
1

Чтобы пройти циклом последовательность в отсортированном порядке, используйте функцию sorted(), которая возвращает отсортированный список, оставляя исходный без изменений.

>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print f
... 	
apple
banana
orange
pear

Подробнее об условиях

[править]

Условия, участвующие в выражениях с if и while могут содержать любые операторы, не только операторы сравнения.

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

Сравнения можно объединять в цепи. Например, a < b == c проверяет, меньше ли a чем b и сверх того — равны ли b и c.

Сравнения могут быть скомпонованы с использованием булевых операций and и or, а результат сравнения (или любого другого булева выражения) можно отрицать используя not. Эти операторы имеют меньший приоритет, чем у операторов сравнения; среди них у not высший приоритет, а у or — низший, поэтому A and not B or C эквивалентно (A and (not B)) or C. Как всегда, скобки помогут задать желаемый порядок операций.

Булевые операторы and и or — это так называемые коротящие операторы[37] (short-circuit operators): их аргументы вычисляются слева направо и вычисление заканчивается как только результат становится определён (очевиден). Например, если A и C являются истинами, а B — ложью, в условии A and B and C выражение C не вычисляется. При использовании в каких-либо общих, не булевых, целях, коротящий оператор возвращает последний элемент, который был вычислен.

Можно присвоить результат сравнения, или другого булева выражения, переменной. Например,

>>> string1, string2, string3 = '', 'Trondheim', 'Hammer Dance'
>>> non_null = string1 or string2 or string3
>>> non_null
'Trondheim'

Заметьте, что в Python, в отличие от C, присваивание не может использоваться внутри выражений. Программисты на C могут возмутиться по этому поводу, но на самом деле это позволяет избежать класса проблем, обычного для программ на C: ввода = в выражении, вместо предполагавшегося ==.

Сравнение последовательностей и других типов

[править]

Объекты последовательностей можно сравнивать с другими объектами с тем же типом последовательности. Сравнение использует лексикографический порядок: сравниваются первые два элемента, и если они различны — результат сравнения определён; если они равны, сравниваются следующие два элемента и так далее до тех пор, пока одна из последовательностей не будет истощена. Если сравниваемые два элемента сами являются последовательностями одного типа, лексикографическое сравнение входит в них рекурсивно. Если все элементы обеих последовательностей оказались равны, последовательности считаются равными. Если одна последовательность оказывается стартовой последовательностью другой, более короткая последовательность считается меньшей. Лексикографическое упорядочивание строк использует порядок в таблице ASCII для индивидуальных символов. Несколько примеров сравнений между однотипными последовательностями:

(1, 2, 3)              < (1, 2, 4)
[1, 2, 3]              < [1, 2, 4]
'ABC' < 'C' < 'Pascal' < 'Python'
(1, 2, 3, 4)           < (1, 2, 4)
(1, 2)                 < (1, 2, -1)
(1, 2, 3)             == (1.0, 2.0, 3.0)
(1, 2, ('aa', 'ab'))   < (1, 2, ('abc', 'a'), 4)

Обратите внимание, что разрешено сравнение объектов разных типов. Итог обусловлен, но кажется произвольным: типы сортируются по их имени. Таким образом, список (list) всегда меньше строки (string), строка всегда меньше кортежа (tuple) и т.д.[38] Смешанные числовые типы сравниваются в соответствии с их численными значениями, так что 0 равен 0.0 и т.д.

Модули

[править]

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

Для поддержки таких решений, в Python есть способ поместить определения в файл и использовать их в сценарии или в интерактивном режиме интерпретатора. Такой файл называется модулем (module); определения из модуля могут быть импортированы в другие модули, либо в главный модуль (коллекция переменных, к которым у вас есть доступ в сценарии, исполняемом на верхнем уровне и в режиме калькулятора).

Модуль — это файл, содержащий определения и выражения на Python. Именем файла является имя модуля с добавленным суффиксом .py. Внутри модуля его имя (в качестве строки) доступно в виде значения глобальной переменной с именем __name__. Например, используя ваш любимый текстовый редактор создайте в текущем каталоге файл с именем fibo.py со следующим содержимым (не забывайте, что первой или второй строкой файла должна быть строка с указанием кодировки):

# -*- coding: iso-8859-15 -*-
# модуль вычисления чисел Фибоначчи

def fib(n):    # вывести числа Фибоначчи вплоть до n
    a, b = 0, 1
    while b < n:
        print b,
        a, b = b, a+b
    print

def fib2(n): # вернуть числа Фибоначчи вплоть до n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

Теперь войдите в интерпретатор Python и импортируйте этот модуль следующей командой:

>>> import fibo

Это действие не переводит имена определённых функций в текущую таблицу символов; здесь вводится лишь имя модуля fibo. Используя имя модуля вы можете получить доступ к функциям:

>>> fibo.fib(1000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

Если вы собираетесь использовать функцию часто, вы можете присвоить её локальному имени:

>>> fib = fibo.fib
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Подробнее о модулях

[править]

Помимо определений функций модуль может содержать исполняемые выражения. Назначение этих выражений — инициализация модуля, они выполняются при первом случае импортирования модуля куда-либо[39].

Каждый модуль имеет свою собственную таблицу символов, которая используется в качестве глобальной всеми определёнными в модуле функциями. Таким образом, автор модуля может использовать глобальные символы в модуле не опасаясь неожиданных совпадений с глобальными переменными пользователя. С другой стороны, если вы знаете что делаете, вы можете сослаться на глобальные переменные модуля пользуясь той же нотацией, которая применялась для ссылок на его функции: <имя_модуля>.<имя_элемента>.

Модули могут импортировать другие модули. Не требуется указывать все операторы import в начале модуля (или сценария, с той же целью), но так обычно и делается. Имена из импортированного модуля добавляются в глобальную таблицу символов модуля его импортирующего.

Есть вариация оператора import, которая переносит имена из модуля прямо в таблицу символов импортирующего модуля. Например:

>>> from fibo import fib, fib2
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

При этом имя самого модуля, из которого переносятся имена элементов, не добавляется в локальную таблицу символов (так, в этом примере, имя fibo не определено)

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

>>> from fibo import *
>>> fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Импортируются все имена, кроме тех, которые начинаются на подчёркивание (_).

Для повышения эффективности, каждый модуль импортируется лишь единожды на каждый сеанс работы с интерпретатором. Поэтому, если вы изменили ваши модули, вам придётся перезапустить интерпретатор — или, если вам нужно интерактивно протестировать только один модуль, используйте reload() таким образом: reload(<имя_модуля>)

Выполнение модулей в качестве сценариев

[править]

Когда вы запускаете модуль Python в виде

python fibo.py <аргументы>

то код в этом модуле будет исполнен в момент его импортирования, но значение __name__ будет установлено как "__main__". Это значит, что добавляя этот код в конец сценария:

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

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

$ python fibo.py 50
1 1 2 3 5 8 13 21 34

Если модуль импортируется, код не будет выполнен:

>>> import fibo
>>>

Такой приём часто используется, чтобы предоставить удобный пользовательский интерфейс к модулю или для тестирования (выполнение модуля в качестве сценария запускает набор тестов).

Путь поиска модулей

[править]

Если импортируется модуль с именем spam, интерпретатор ищет файл с именем spam.py в текущем каталоге, а затем в каталогах, указанных в переменной окружения PYTHONPATH. У неё такой же синтаксис, как и у переменной шелла PATH, которая, в свою очередь, является перечислением каталогов. Когда переменная PYTHONPATH не установлена, или файл не найден в описанных в ней местах, поиск продолжается по пути по умолчанию, зависящему от указанного при установке; на Unix это обычно .:/usr/local/lib/python.

В действительности, поиск модулей производится в списке каталогов, передающемся в переменной sys.path, которая инициируется: каталогом, содержащим сценарий на входе (или текущий каталог), PYTHONPATH и умолчанием для каталога, указанного при установке. Это позволяет программам на Python (которые знают что делают) изменять или подменять путь поиска модулей. Заметьте — поскольку каталог, содержащий запускаемый вами сценарий, также находится в пути поиска — важно чтобы имя сценария не совпадало с именем стандартного модуля, иначе — когда этот модуль будет импортироваться — Python будет пытаться загрузить в виде модуля сам сценарий, в большинстве случаев это будет ошибкой. Для более подробной информации обратитесь к разделу Стандартные модули.

„Скомпилированные“ файлы Python

[править]

В качестве важного способа ускорения запуска короткой программы, использующей много стандартных модулей может выступать условие: если в каталоге, где располагается файл spam.py найден также файл spam.pyc, последний предполагается уже «скомпилированной-побайтно» („byte-compiled“) версией модуля spam. В файле spam.pyc записано время изменения версии spam.py, использовавшейся для создания spam.pyc, и если версии не совпадают — файл .pyc игнорируется.

В обычном случае, вам не нужно ничего делать для создания файла spam.pyc. Каждый раз, когда spam.py успешно компилируется, предпринимается попытка записать скомпилированную версию в spam.pyc. Не считается ошибкой, если попытка неудачна; если по какой-либо причине файл не записан полностью, результирующий файл spam.pyc будет считаться некорректным и по этой причине в дальнейшем игнорироваться. Содержимое файла spam.pyc платформо-независимо, благодаря чему каталог модулей Python может использоваться параллельно машинами с различной архитектурой.

Несколько советов экспертам:

  • Когда интерпретатор Python запускается с флагом -O, в файлах .pyo сохраняется сгенерированный оптимизированный код. На данный момент оптимизатор помогает не сильно — он лишь удаляет операторы assert. В случае если используется -O, оптимизируется весь байт-код (bytecode); файлы .pyc игнорируются, а файлы .py компилируются в оптимизированный байт-код.
  • Передача двух флагов -O интерпретатору Python (-OO) принуждает компилятор байт-кода выполнять оптимизации, в редких случаях результат выполнения которых оказывается некачественно функционирующей программой. На данный момент из байт-кода удаляются только строки __doc__, в результате получаются более компактные файлы .pyo. Поскольку некоторые программы могут рассчитывать на их (строк) доступность, следует использовать эту возможность только в том случае, если вы знаете что делаете.
  • Программа сама по себе не работает хоть сколь-нибудь быстрее, будучи прочитанной из файла .pyc или .pyo, чем если бы она была прочитана из файла .py. Единственный процесс, оказывающийся более быстрым при использовании файлов .pyc или .pyo — это скорость их подгрузки.
  • Если сценарий запущен за счёт передачи его имени командной строке, его байт-код никогда не будет записан в файл .pyc или .pyo. Таким образом, время запуска сценария может быть уменьшено засчёт перемещения большей части его кода в модуль и использования небольшого загрузочного сценария, импортирующего этот модуль. Также возможно указывать файл .pyc или .pyo прямо в командной строке.
  • Можно иметь в наличии файл с именем spam.pyc (или spam.pyo, когда используется -O), не имея файла spam.py для того же модуля. Таким образом, можно распространять библиотеки кода Python в том виде, из которого трудно восстановить исходный код.
  • Модуль compileall может создать файлы .pyc (или файлы .pyo, когда используется -O) для всех модулей в каталоге.

Стандартные модули

[править]

Python поставляется с библиотекой стандартных модулей, описанной в отдельном документе, Справочнике по библиотеке Python (далее — «Справочнику по библиотеке»). Некоторые модули встроены в интерпретатор; они обеспечивают доступ к операциям, не являющимся ядром языка, но несмотря на это, они встроены для большей эффективности и предоставления доступа к примитивам операционной системы, таким как системные вызовы (system calls). Набор таких модулей — выбор настройки, который также зависит от используемой платформы. Например, модуль winreg предоставляется только на тех системах, на которые установлен Windows. Один конкретный модуль заслуживает большего внимания: модуль sys, встроенный в каждую версию интерпретатора Python. Переменные sys.ps1 и sys.ps2 определяют строки, использующиеся в качестве основного и вспомогательного приглашений:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print 'Yuck!'
Yuck!
C>

Эти две переменные определены только если интерпретатор находится в интерактивном режиме.

Переменная sys.path представляет собой список строк, определяющий путь поиска модулей интерпретатора. Она инициируется путём по умолчанию, взятым из переменной окружения PYTHONPATH, или встроенным значением по умолчанию, если PYTHONPATH не установлен. Вы можете изменить её значение, используя стандартные операции со списками:

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

Функция dir()

[править]

Встроенная функция dir() используется для получения имён, определённых в модуле. Она возвращает отсортированный список строк:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)
['__displayhook__', '__doc__', '__excepthook__', '__name__', '__stderr__',
 '__stdin__', '__stdout__', '_getframe', 'api_version', 'argv',
 'builtin_module_names', 'byteorder', 'callstats', 'copyright',
 'displayhook', 'exc_clear', 'exc_info', 'exc_type', 'excepthook',
 'exec_prefix', 'executable', 'exit', 'getdefaultencoding', 'getdlopenflags',
 'getrecursionlimit', 'getrefcount', 'hexversion', 'maxint', 'maxunicode',
 'meta_path', 'modules', 'path', 'path_hooks', 'path_importer_cache',
 'platform', 'prefix', 'ps1', 'ps2', 'setcheckinterval', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'settrace', 'stderr', 'stdin', 'stdout',
 'version', 'version_info', 'warnoptions']

Будучи использованной без аргументов, функция dir() возвращает список имён, определённых в данный момент.

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__doc__', '__file__', '__name__', 'a', 'fib', 'fibo', 'sys']

Обратите внимание, что список состоит из имён всех типов: переменных, модулей, функций и т.д.

В списке, возвращаемом функцией dir() не содержится встроенных функций и переменных. Если вы хотите получить их список, то они определены в стандартном модуле __builtin__:

>>> import __builtin__
>>> dir(__builtin__)
['ArithmeticError', 'AssertionError', 'AttributeError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FloatingPointError', 'FutureWarning', 'IOError', 'ImportError',
 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt',
 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError',
 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError',
 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True',
 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError',
 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError',
 'UserWarning', 'ValueError', 'Warning', 'WindowsError',
 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__',
 '__name__', 'abs', 'apply', 'basestring', 'bool', 'buffer',
 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile',
 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod',
 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float',
 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex',
 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter',
 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'min',
 'object', 'oct', 'open', 'ord', 'pow', 'property', 'quit', 'range',
 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set',
 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super',
 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']

Пакеты

[править]

Пакеты — способ структурирования пространств имён (namespaces) модулей Python за счёт использования имён модулей, разделённых точками („dotted module names“). Например, имя модуля A.B означает — подмодуль[40] с именем B в пакете с именем A. Также как использование модулей позволяет авторам различных модулей не заботиться о пересекающихся именах среди глобальных переменных, использование именования через точку позволяет авторам многомодульных пакетов (таких как NumPy или Python Imaging Library) не заботиться о пересечении имён модулей.

Допустим, вы собираетесь разработать набор модулей (пакет, package) для унифицированной работы со звуковыми файлами и звуковыми данными. Существует множество форматов звуковых файлов (обычно их можно распознать по расширению, например: .wav, .aiff, .au) — таким образом, вам может понадобиться создать и поддерживать разрастающуюся коллекцию модулей для конвертирования между различными форматами файлов. Также вам наверняка захочется иметь побольше операций для обработки звуковых данных (таких как смешивание, добавление эха, применение функции эквалайзера, создание искусственного стерео-эффекта), так что в дополнение к этому вы будете писать нескончаемый поток модулей для исполнения этих операций. Вот возможная структура вашего пакета (выраженная в терминологии иерархической файловой системы):

sound/                          Пакет верхнего уровня
      __init__.py               Инициализация пакета работы со звуком (sound)
      formats/                  Подпакет для конвертирования форматов файлов
              __init__.py
              wavread.py        (чтение wav)
              wavwrite.py       (запись wav)
              aiffread.py       (чтение aiff)
              aiffwrite.py      (запись aiff)
              auread.py         (чтение au)
              auwrite.py        (запись au)
              ...
      effects/                  Подпакет для звуковых эффектов
              __init__.py
              echo.py           ( эхо )
              surround.py       ( окружение )
              reverse.py        ( обращение )
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py      ( эквалайзер )
              vocoder.py        ( вокодер )
              karaoke.py        ( караоке )
              ...

Если импортируется пакет, Python ищёт подкаталог пакета в каталогах, перечисленных в sys.path.

Файлы __init__.py необходимы для того, чтобы Python трактовал эти каталоги как содержащие пакеты; это сделано чтобы избежать ненамеренного сокрытия правомерных модулей, встречающихся в дальнейшем по пути поиска, каталогами с часто используемыми именами, таким как "string". В наипростейшем случае файл __init__.py может быть пустым, но в более сложных может содержать код инициализации пакета или устанавливать значение описанной ниже переменной __all__.

Пользователи пакета могут импортировать из него конкретные модули, например:

import sound.effects.echo

Таким образом подгружается подмодуль sound.effects.echo. Ссылаться на него нужно используя его полное имя:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Другой способ импортирования подмодуля:

from sound.effects import echo

Так тоже подгружается подмодуль echo, но теперь он доступен без префикса пакета, поэтому может использоваться следующим образом:

echo.echofilter(input, output, delay=0.7, atten=4)

И еще один вариант — прямое импортирование желаемой функции или переменной:

from sound.effects.echo import echofilter

Опять же, таким образом подгружается подмодуль echo, но теперь его функция echofilter() может быть вызвана непосредственно:

echofilter(input, output, delay=0.7, atten=4)

Заметьте, что при использовании выражения from пакет import элемент, элементом может быть подмодуль (или подпакет) пакета или любое другое имя, определённое в пакете - например, функция, класс или переменная. Оператор import сначала проверяет, определён ли элемент в пакете; если нет — он трактует его как модуль и пытается загрузить. Если не удается его найти, порождается исключение ImportError.

Напротив, при использовании синтаксиса в стиле import элемент.подэлемент.подэлемент, все элементы кроме последнего должны быть пакетами; последний элемент может быть модулем или пакетом, но не может быть классом, функцией или переменной, определёнными в предыдущем элементе.

Импорт * из пакета

[править]

Что происходит, когда пользователь пишет from sound.effects import * ? В идеале, мы бы надеялись, что таким образом код выходит в файловую систему и находит какие подмодули существуют в пакете, импортируя их все. К сожалению, такой метод не очень хорошо работает на платформах Windows, поскольку у файловой системы не всегда есть корректная информация о регистре имён файлов. На этих платформах нет гарантированного способа узнать, нужно ли импортировать файл ECHO.PY в качестве модуля echo, Echo или ECHO. (Например, у Windows 95 есть назойливая привычка показывать имена всех файлов с заглавной буквы.) Ограничение DOS на имя файла в формате 8+3 добавляет забавную проблему, связанную с длинными именами модулей.

Единственный выход для автора пакета — предоставить его подробное содержание. Оператор import использует следующее соглашение: если в коде файла __init__.py текущего пакета определён список __all__, то он полагается списком имён модулей, которые нужно импортировать если обнаружено выражение from пакет import *. На совести автора поддержка этого списка в соответствующем состоянии в каждой новой версии пакета. Впрочем, авторы пакета могут его не поддерживать вообще, если не видят смысла в импортировании * из их пакета. Например, файл sounds/effects/__init__.py может содержать следующий код:

__all__ = ["echo", "surround", "reverse"]

Это будет значить, что выражение from sound.effects import * импортирует три именованных подмодуля из пакета sound.

Если список __all__ не определён, выражение from Sound.Effects import * не импортирует все подмодули пакета sound.effects в текущее пространство имён — оно лишь убеждается, что импортирован пакет sound.effects (возможно, выполняя код инициализации из __init__.py), а затем импортирует все определённые в пакете имена. В этот список попадают любые имена, определённые (и загруженные явно подмодулями) в __init__.py. В него также попадают все явно загруженные предшествующими операторами import подмодули. Рассмотрим следующий код:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

В этом примере, модули echo и surround импортируются в текущее пространство имён, поскольку они определены в пакете sound.effects на тот момент, когда исполняется оператор from ... import. (И это также работает если определён __all__.)

Обратите внимание, что в общем случае импортирование * из модуля не приветствуется, поскольку в результате часто получается плохо-читаемый код. Однако, вполне нормально использовать его в интерактивных сессиях чтобы меньше печатать и определённые модули разработаны для экспорта только тех имён, которые следуют определённым шаблонам.

Помните, в использовании from пакет import определённый_подмодуль нет ничего плохого. На самом деле — это рекомендованная запись, до тех пор пока при импортировании модуля не нужно использовать подмодулей с одинаковым именем из разных пакетов.

Внутрипакетные ссылки

[править]

Часто подмодулям необходимо ссылаться друг на друга. В качестве примера, модуль surround может использовать модуль echo. Фактически, такие ссылки настолько привычны, что оператор import ищет элемент по содержащему модуль пакету, прежде чем обращаться к стандартному пути поиска. Так, модуль surround может использовать просто import echo или from echo import echofilter. Если импортируемый модуль не найден в текущем пакете (пакете, для которого текущий модуль является подмодулем), оператор import ищет верхнеуровневый модуль с именем, идентичным переданному.

Когда пакеты структурированы в подпакеты (например, в случае пакета Sound), не существует короткой формы записи для ссылки на пакеты-потомки - нужно использовать полное имя подпакета. Например, если модуль Sound.Filters.vocoder нуждается в модуле echo из пакета Sound.Effects, он должен использовать from Sound.Effects import echo.

Начиная с Python 2.5, в дополнение к описанным выше способам неявного относительного импортирования (implicit relative imports), вы можете использовать явное относительное импортирование (explicit relative imports) пользуясь формой from модуль import имя оператора import. Такие способы импортирования используют символы точки для описания текущего и родительского пакетов, порожденных относительным импортированием. Например, для модуля surround вы можете написать:

from . import echo
from .. import Formats
from ..Filters import equalizer

Обратите внимание, что и явное и неявное относительное импортирование основано на имени текущего модуля. Поскольку имя главного модуля всегда „__main__“ — модули, предназначенные для использования в качестве главных модулей приложения на Python, должны всегда использовать абсолютное импортирование (absolute imports).

Пакеты в нескольких каталогах

[править]

Пакеты поддерживают ещё один специальный атрибут: __path__. Перед исполнением файла __init__.py этого пакета, он инициализируется списком, содержащим имя каталога, в котором этот файл находится. Переменную можно изменять — тогда изменится будущее поведение поиска модулей и подпакетов, содержащихся в пакете.

Хотя эта возможность нужна нечасто, она может быть использована для расширения набора модулей, находящихся в пакете.

Ввод и вывод

[править]

Ознакомить пользователя с выводом программы можно различными способами — данные могут быть выведены в читабельном виде или записаны в файл для последующего использования. Часть возможностей будет обсуждена в этой главе.

Прихотливое форматирование вывода

[править]

На данный момент мы выяснили два способа вывода значений: выражения с операторами (expression statements) и оператор print. (Третий способ — использование метода write() объектов файлов; на файл стандартного вывода можно сослаться как на sys.stdout. Более подробную информацию по этому пункту смотрите в Справочнике по библиотеке.)

Часто возникает желание иметь больший контроль над форматированием вывода, чем обычная печать значений разделённых пробелами. Есть два способа форматирования вашего вывода. Первый способ — выполнять самостоятельно всю работу над строками: используя срезы строк и конкатенацию вы можете создать любой шаблон какой пожелаете. Стандартный модуль string содержит много полезных операций для выравнивания строк по определённой ширине колонки — скоро мы их кратко рассмотрим. Второй способ — использование метода str.format().

Остаётся, конечно, один вопрос: каким образом вам конвертировать значения в строки? К счастью, в Python есть пути для конвертации любого значения в строку: передайте его функциям repr() или str().

Предназначение функции str() — возврат значений в довольно-таки читабельной форме; в отличие от repr() , чьё назначение — генерирование форм[41], которые могут быть прочитаны интерпретатором (или вызовут ошибку SyntaxError, если эквивалентного синтаксиса не существует). Для тех объектов, у которых нет формы для человеческого прочтения, функция str() возвратит такое же значение, какое возвратит repr(). У многих значений, таких как числа или структуры, вроде списков и словарей, одинаковая форма для обеих функций. Строки и числа с плавающей точкой, в частности, имеют по две разных формы.

Несколько примеров:

>>> s = 'Привет, мир.'
>>> str(s)
'Привет, мир.'
>>> repr(s)
"'Привет, мир.'"
>>> str(0.1)
'0.1'
>>> repr(0.1)
'0.10000000000000001'
>>> x = 10 * 3.25
>>> y = 200 * 200
>>> s = 'Значение x - ' + repr(x) + ', а y - ' + repr(y) + '...'
>>> print s
Значение x - 32.5, а y - 40000...
>>> # Функция repr(), применённая к строке, добавляет кавычки и обратные слэши:
... hello = 'привет, мир\n'
>>> hellos = repr(hello)
>>> print hellos
'привет, мир\n'
>>> # Аргументом функции repr() может быть объект Python:
... repr((x, y, ('фарш', 'яйца')))
"(32.5, 40000, ('фарш', 'яйца'))"

Вот два способа вывести таблицу квадратов и кубов:

>>> for x in range(1, 11):
...     print repr(x).rjust(2), repr(x*x).rjust(3),
...     # Заметьте замыкающую предыдущую строку запятую
...     print repr(x*x*x).rjust(4)
...
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000
>>> for x in range(1,11):
...     print '%2d %3d %4d' % (x, x*x, x*x*x)
... 
 1   1    1
 2   4    8
 3   9   27
 4  16   64
 5  25  125
 6  36  216
 7  49  343
 8  64  512
 9  81  729
10 100 1000

(Обратите внимание, что в первом примере единичные пробелы между колонок добавлены за счёт принципов работы оператора print: он всегда вставляет пробелы между своими аргументами)

Этот пример демонстрирует работу метода объектов строк rjust(), выравнивающего строку по правому краю в поле переданной ширины, отступая пробелами слева. Есть также похожие методы ljust() и center(). Эти методы не выводят ничего, они лишь возвращают новую строку. Если строка на входе чересчур длинная, то они не усекают её, а возвращают неизменённой; таким образом ваши колонки могут перемешаться, но это обычно лучшая идея чем её альтернативы, которые могут некорректно обработать значение. (Если вы действительно хотите использовать усечение, вы всегда можете добавить операцию среза, как в таком примере: x.ljust(n)[:n].)

Есть другой метод, zfill(), который заполняет нулями пространство слева от числовой строки. Он распознаёт знаки плюс и минус:

>>> '12'.zfill(5)
'00012'
>>> '-3.14'.zfill(7)
'-003.14'
>>> '3.14159265359'.zfill(5)
'3.14159265359'

Основной способ применения метода str.format() выглядит так[42]:

>>> print 'Мы — те {0}, что говорят "{1}!"'.format('рыцари', 'Ни')
Мы  те рыцари, что говорят "Ни!"

Скобки и символы, которые они окружают (их называют полями форматирования (format fields)) заменяются на объекты, переданные в метод format. Номер в скобках обозначает позицию объекта в списке аргументов, переданных в метод format.

>>> print '{0} и {1}'.format('фарш', 'яйца')
фарш и яйца
>>> print '{1} и {0}'.format('фарш', 'яйца')
яйца и фарш

Если в методе format используются именованные аргументы, ссылаться на их значения можно используя имя соответствующего аргумента[43].

>>> print 'Этот {food}{adjective}.'.format(
...       food='фарш', adjective='непередаваемо ужасен')
Этот фарш  непередаваемо ужасен.

Позиционные и именованные аргументы можно произвольно совмещать[44]:

>>> print 'История о {0}е, {1}е, и {other}е.'.format('Билл', 'Манфред',
                                                       other='Георг')
История о Билле, Манфреде, и Георге.

После имени поля может следовать необязательный спецификатор формата ‘:’. Пользуясь им, можно управлять тем, как значение будет отформатировано. Следующий пример оставляет у числа Пи только три цифры после десятичного разделителя[45].

>>> import math
>>> print 'Значение ПИ — примерно {0:.3f}.'.format(math.pi)
Значение ПИ  примерно 3.142.

Если после спецификатора ‘:’ указать число — оно будет обозначать минимальную ширину поля, выраженную в количестве символов. Это удобно использовать для создания приятных для прочтения таблиц:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 7678}
>>> for name, phone in table.items():
...     print '{0:10} ==> {1:10d}'.format(name, phone)
...
Jack       ==>       4098
Dcab       ==>       7678
Sjoerd     ==>       4127

Если ваша строка с форматами очень длинна, а вы не хотите разбивать её на подстроки, было бы неплохо если бы вы могли ссылаться на переменные, предназначенные для форматирования, не по позиции, а по имени. Это можно сделать, просто передав словарь и используя квадратные скобки ‘[]’ для доступа к ключам.

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
          'Dcab: {0[Dcab]:d}'.format(table)
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Также это можно сделать, передав таблицу в виде именованных аргументов, используя нотацию „**“:

>>> table = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 8637678}
>>> print 'Jack: {Jack:d}; Sjoerd: {Sjoerd:d}; Dcab: {Dcab:d}'.format(**table)
Jack: 4098; Sjoerd: 4127; Dcab: 8637678

Это, в отдельности, довольно полезно использовать в сочетании с новой встроенной функцией vars(), которая возвращает словарь, содержащий все локальные переменные.

Подробное описание форматирования строк с применением метода str.format() описано в разделе Синтаксис строк форматирования.

Форматирование строк в старом стиле

[править]

Для форматирования строк можно использовать и оператор %. Он интерпретирует левый аргумент как строку форматирования в стиле sprintf, которую следует применить к правому аргументу, и возвращает строку, получившуюся за счёт этого преобразования. Например:

>>> import math
>>> print 'Значение ПИ — примерно %5.3f.' % math.pi
Значение ПИ  примерно 3.142.

Поскольку метод str.format() довольно нов, большая часть исходных кодов Python всё ещё использует оператор %. Однако, со временем, форматирование строк будет удалено из языка, поэтому в большинстве случаев следует использовать str.format().

Больше информации можно найти в разделе Операции форматирования строк.

Запись и чтение файлов

[править]

Функция open() возвращает объект файла и в большинстве случаев используется с двумя аргументами: open(имя_файла, режим).

>>> f = open('/tmp/workfile', 'w')
>>> print f
<open file '/tmp/workfile', mode 'w' at 80a0960>

Первый аргумент — строка, содержащая имя файла. Второй аргумент — другая строка, содержащая несколько символов, описывающих каким образом файл будет использоваться. Значение аргумента режим может быть символом 'r', если файл будет открыт только для чтения, 'w' — открыт только для записи (существующий файл с таким же именем будет стёрт), и 'a' — файл открыт для добавления: любые данные, записанные в файл автоматически добавляются в конец. 'r+' открывает файл и для чтения и для записи. Аргумент режим необязателен: если он опущен — предполагается, что он равен 'r'.

При использовании ОС Windows, символ 'b', добавленный к аргументу режим, открывает файл в двоичном режиме (binary mode), так что существуют такие режимы как 'rb', 'wb' и 'r+b'. Для платформы Windows есть разница между текстовыми и двоичными файлами — символы конца строки в текстовых файлах сильно модифицируются при записи или чтении данных. Изменение данных файла «за кулисами» нормально работает для текстовых файлов в ASCII, но повреждает двоичные данные, такие как в файлах JPEG или EXE. Будьте осторожны и используйте двоичный режим при чтении и записи таких файлов. На системах Unix, добавление символа 'b' к режиму не причиняет никаких неудобств, так что вы можете использовать его для всех двоичных файлов, независимо от платформы.

Методы объектов File

[править]

Во всех оставшихся в этом разделе примерах подразумевается, что заранее создан объект файла с именем f.

Чтобы прочитать содержимое файла вызовите f.read(размер) — функция читает некоторое количество данных и возвращает их в виде строки. размер — необязательный числовой аргумент. Если размер опущен или отрицателен, будет прочитано и возвращено всё содержимое файла; если файл по величине в два раза больше оперативной памяти вашего компьютера, то решение этой проблемы остаётся на вашей совести. В обратном случае, будет прочитано и возвращено максимум размер байт. Если был достигнут конец файла, f.read() вернёт пустую строку ("").

>>> f.read()
'Это всё содержимое файла.\n'
>>> f.read()
''

f.readline() читает одну строку из файла; символ новой строки (\n) остаётся в конце прочитанной строки и отсутствует при чтении последней строки файла только если файл не оканчивается пустой строкой. За счёт этого возвращаемое значение становится недвусмысленным: если f.readline() возвращает пустую строку — достигнут конец файла, в то же время незаполненная строка, представленная посредством '\n', содержит лишь символ новой строки.[46]

>>> f.readline()
'Это первая строка файла.\n'
>>> f.readline()
'Вторая строка файла\n'
>>> f.readline()
''

f.readlines() возвращает список, содержащий все строки с данными, обнаруженные в файле. Если передан необязательный параметр подсказка_размера, функция читает из файла указанное количество байт, плюс некоторое количество байт сверх, достаточное для завершения строки, и формирует список строк из результата. Функция часто используется для построчного чтения больших файлов, эффективного за счёт отсутствия необходимости загрузки файла в память полностью. Возвращены будут только полные (завершённые) строки.

>>> f.readlines()
['Это первая строка файла.\n', 'Вторая строка файла\n']

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

>>> for line in f:
        print line,
        
Это первая строка файла.
Вторая строка файла

Альтернативный способ проще, но не предоставляет тонкого контроля над происходящим. Поскольку оба этих способа работают с буферизацией строк по разному, не следует их смешивать.

f.write(строка) записывает содержимое строки в файл и возвращает None.

>>> f.write('Это тест\n')

Чтобы записать в файл нечто отличное от строки, предварительно это нечто нужно в строку сконвертировать[47]:

>>> value = ('ответ', 42)
>>> s = str(value)
>>> f.write(s)

f.tell() возвращает целое, представляющее собой текущую позицию в файле объекта файла f, измеренную в байтах от начала файла. Чтобы изменить позицию объекта файла, используйте "f.seek(смещение, откуда)". Позиция вычисляется за счёт сложения смещения и точки отсчёта; точка отсчёта выбирается из аргумента откуда. Значение 0 аргумента откуда отмеряет смещение от начала файла, значение 1 применяет текущую позицию в файле, а значение 2 в качестве точки отсчёта использует конец файла. Аргумент откуда может быть опущен и по умолчанию устанавливается в 0, используя начало файла в качестве точки отсчёта.

>>> f = open('/tmp/workfile', 'r+')
>>> f.write('0123456789abcdef')
>>> f.seek(5)     # Перейти к шестому байту в файле
>>> f.read(1)        
'5'
>>> f.seek(-3, 2) # Перейти к третьему байту с конца 
>>> f.read(1)
'd'

Когда вы закончили все действия над файлом, вызовите f.close() чтобы закрыть его и освободить все системные ресурсы, использованные при открытии этого файла. Все попытки использовать объект файла после вызова f.close() будут завершены неудачей.

>>> f.close()
>>> f.read()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: I/O operation on closed file

Считается хорошей манерой использовать ключевое слово with при работе с объектами файлов. Преимущество этого способа в том, что файл всегда корректно закрывается после выполнения блока, либо если при выполнении было порождено исключение. Кроме того, получающийся код намного короче, чем эквивалентная форма с блоками try-finally:

>>> with open('/tmp/workfile', 'r') as f:
...     read_data = f.read()
>>> f.closed
True

У объектов файла есть ещё несколько дополнительных методов, таких как isatty() и truncate(), которые используются не так часто; обратитесь к Справочнику по библиотеке для более полного обзора по объекту файла.

Модуль pickle

[править]

Строки могут быть с лёгкостью записаны в файл или прочитаны из файла. В случае чисел нужно применить несколько больше усилий, поскольку метод read() возвращает только строки. Их придётся передать функции вроде int(), которая принимает строку в виде '123' и возвращает её числовое значение: 123. Однако, если вы намереваетесь сохранить более сложные типы данных, такие как списки, словари или экземпляры классов, всё становится несколько запутаннее.

Вместо того, чтобы принуждать программиста постоянно писать и отлаживать код для замысловатых типов данных, Python предоставляет стандартный модуль под названием pickle. Это замечательный модуль, который может принять любой объект Python (даже некоторые формы кода на Python!) и конвертировать его в строковое представление: этот процесс называется консервацией (pickling). Восстановление объекта из его строкового представления называется расконсервацией (unpickling): строка, описывающая объект, может быть сохранена в файл, добавлена к некоторым данным, или отослана через соединение по сети на удаленный компьютер.[48]

Если у вас есть некоторый объект x и объект файла f, открытый на запись, простейший способ законсервировать объект требует одной-единственной строки кода:

pickle.dump(x, f)

Чтобы снова расконсервировать объект, при условии, что f — объект файла, открытого для чтения:

x = pickle.load(f)

(Существуют варианты выполнения этих операций, применяемые при расконсервации нескольких объектов или когда вам требуется записать консервированные данные в файл; проконсультируйтесь с полной документацией по модулю pickle из Справочника по библиотеке.)

pickle — стандартный способ для создания объектов Python, которые могут быть переиспользованы другими программами или будущими версиями этой же программы; для них есть технический термин — устойчивый объект (persistent object). Поскольку pickle используется часто, многие авторы расширений для Python заботятся о том, чтобы новые типы данных, такие как матрицы, могли быть корректно законсервированы и расконсервированы.

Ошибки и исключения

[править]

До этого момента сообщения об ошибках лишь упоминались, но если вы пробовали примеры на практике — возможно, вы уже видели некоторые. Существует (как минимум) два различимых вида ошибок: синтаксические ошибки (syntax errors) и исключения (exceptions).

Синтаксические ошибки

[править]

Синтаксические ошибки, также известные как ошибки разбора кода (парсинга, parsing) — вероятно, наиболее привычный вид протестов интерпретатора, попадающихся вам при изучении Python:

>>> while True print 'Hello world'
  File "<stdin>", line 1, in ?
    while True print 'Hello world'
                   ^
SyntaxError: invalid syntax

Парсер[49] повторно выводит возмутившую его строку и отображает небольшую «стрелку», указывающую на самую первую позицию в строке, где была обнаружена ошибка. Причина ошибки (или, по крайней мере, место обнаружения) находится в символе[50], предшествующем указанному: в приведённом примере ошибка обнаружена на месте вызова оператора print, поскольку перед ним пропущено двоеточие (':'). Также здесь выводятся имя файла и номер строки, благодаря этому вы знаете в каком месте искать, если ввод был сделан из сценария.

Исключения

[править]

Даже если выражение или оператор синтаксически верны, они могут вызвать ошибку при попытке их исполнения. Ошибки, обнаруженные при исполнении, называются исключениями (exceptions) и не фатальны безоговорочно: позже вы научитесь перехватывать их в программах на Python. Большинство исключений, правда, как правило, не обрабатываются программами и приводят к сообщениям об ошибке, таким как следующие:

>>> 10 * (1/0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ZeroDivisionError: integer division or modulo by zero
>>> 4 + spam*3
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'spam' is not defined
>>> '2' + 2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: cannot concatenate 'str' and 'int' objects

Последняя строка сообщения об ошибке описывает произошедшее. Исключения представлены различными типами и тип исключения выводится в качестве части сообщения: в примере это типы ZeroDivisionError, NameError и TypeError. Часть строки, описывающая тип исключения — это имя произошедшего встроенного исключения. Такое утверждение истинно для всех встроенных исключений, но не обязано быть истинным для исключений, определённых пользователем (однако, само соглашение — довольно полезное). Имена стандартных исключений это встроенные идентификаторы (не ключевые слова).

Оставшаяся часть строки описывает детали произошедшего на основе типа исключения, которое было его причиной.

Предшествующая часть сообщения об ошибке показывает контекст, где произошло исключение, в форме стека вызовов. В общем случае она содержит стек, состоящий из списка строк исходного кода; тем не менее, в неё не войдут строки, прочитанные из стандартного ввода.

В разделе Встроенные исключения Справочника по библиотеке вы найдёте список встроенных исключений и их значений.

Обработка исключений

[править]

Существует возможность написать код, который будет перехватывать избранные исключения. Посмотрите на представленный пример, в котором пользователю предлагают вводить число до тех пор, пока оно не окажется корректным целым. Тем не менее пользователь может прервать программу (используя сочетание клавиш Control-C или какое-либо другое, поддерживаемое операционной системой); заметьте — о вызванном пользователем прерывании сигнализирует исключение KeyboardInterrupt.

>>> while True:
...     try:
...         x = int(raw_input("Введите, пожалуйста, число: "))
...         break
...     except ValueError:
...         print "Ой!  Это некорректное число.  Попробуйте ещё раз..."
...

Оператор try работает следующим образом:

  • Сначала исполняется блок try (операторы(ы) между ключевыми словами try и except).
  • Если при этом не появляется исключений, блок except не выполняется и оператор try заканчивает работу.
  • Если во время выполнения блока try появилось какое-либо исключение, оставшаяся часть блока не выполняется. Затем, если тип этого исключения совпадает с исключением, указанным после ключевого слова except — выполняется блок except, а по его завершению выполнение продолжается сразу после оператора try.
  • Если порождается исключение, не совпадающее по типу с указанным в блоке except — оно передаётся внешним операторам try; если ни одного обработчика не найдено, исключение считается необработанным (unhandled exception), выполнение полностью останавливается и выводится сообщение, схожее с показанным выше.

Оператор try может иметь более одного блока except — для описания обработчиков различных исключений. При этом будет выполнен максимум один обработчик. Обработчики следят[51] только за теми исключениями, которые появляются в соответствующем блоке try, не за порождёнными в других обработчиках. Блок except может указывать несколько исключений в виде заключённого в скобки кортежа, например:

... except (RuntimeError, TypeError, NameError):
...     pass

В последнем блоке except можно не указывать имени (имён) исключений, тогда он будет действовать как обработчик группы исключений. Используйте эту возможность с особой осторожностью, поскольку таким образом он может с лёгкостью перехватить и фактическую ошибку программиста! Также такой обработчик может быть использован для вывода сообщения об ошибке и порождения исключения заново (при этом позволяя обработать исключение коду, вызвавшему обработчик):

import sys

try:
    f = open('myfile.txt')
    s = f.readline()
    i = int(s.strip())
except IOError as (errno, strerror):
    print "I/O error({0}): {1}".format(errno, strerror)
except ValueError:
    print "Не могу преобразовать данные в целое."
except:
    print "Неожиданная ошибка:", sys.exc_info()[0]
    raise

У оператора try ... except есть необязательный блок else, который, если присутствует, должен размещаться после всех блоков except. Его полезно использовать при наличии кода, который должен быть выполнен, если блок try не породил исключений. Например:

for arg in sys.argv[1:]:
    try:
        f = open(arg, 'r')
    except IOError:
        print 'не могу открыть', arg
    else:
        print arg, 'содержит', len(f.readlines()), 'строк'
        f.close()

Использование блока else предпочтительнее, чем добавление дополнительного кода к блоку try, поскольку исключает неожиданный перехват исключения, которое появилось не по причине выполнения кода, защищенного оператором try ... except.

При появлении исключения, оно может иметь ассоциированное значение, также известное как аргумент (argument) исключения. Присутствие и тип аргумента зависят от типа самого исключения.

В блоке except можно указать переменную, следующую за именем исключения (или кортежем). Переменная связывается с экземпляром исключения, аргументы которого хранятся в instance.args. Для удобства, экземпляр исключения определяет методы __getitem__ и __str__, так что доступ к аргументам или их вывод могут быть произведены явно, без необходимости отсылки к .args.

Использование .args в целом не одобряется. Вместо этого, рекомендуемый способ использования — это передача исключению единственного аргумента (который может быть кортежем, если требуется несколько аргументов) и его связь с атрибутом message. Таким образом можно создать экземпляр исключения перед его порождением (raising) и добавить любые атрибуты по желанию.

>>> try:
...    raise Exception('spam', 'eggs')
... except Exception as inst:
...    print type(inst)     # экземпляр исключения
...    print inst.args      # аргументы хранятся в .args
...    print inst           # __str__ позволяет вывести args явно
...    x, y = inst          # __getitem__ позволяет распаковать args явно
...    print 'x =', x
...    print 'y =', y
...
<type 'instance'>
('spam', 'eggs')
('spam', 'eggs')
x = spam
y = eggs

Если у исключения есть аргумент, он выводится в качестве последней части («детальной») сообщения необработанных исключений.

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

>>> def this_fails():
...     x = 1/0
... 
>>> try:
...     this_fails()
... except ZeroDivisionError as detail:
...     print 'Перехват ошибки времени исполнения:', detail
... 
Перехват ошибки времени исполнения: integer division or modulo by zero

Порождение исключений

[править]

Оператор raise позволяет программисту принудительно породить исключение. Например:

>>> raise NameError, 'ПриветТам'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: ПриветТам

Первый аргумент оператора raise именует исключение, которое нужно породить. Необязательный второй аргумент определяет аргумент исключения. Кроме того, код выше может быть записан в виде raise NameError('ПриветТам'). Обе формы работают корректно, однако второму варианту, похоже, отдаётся стилистическое предпочтение.

Если вам нужно определить, было ли порождено исключение, но при этом не перехватывать его — упрощённая форма оператора raise позволяет вам породить исключение заново:

>>> try:
...     raise NameError, 'ПриветТам'
... except NameError:
...     print 'Исключение пролетело мимо!'
...     raise
...
Исключение пролетело мимо!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: ПриветТам

Исключения, определённые пользователем

[править]

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

>>> class MyError(Exception):
...     def __init__(self, value):
...         self.value = value
...     def __str__(self):
...         return repr(self.value)
... 
>>> try:
...     raise MyError(2*2)
... except MyError as e:
...     print 'Поймано моё исключение со значением:', e.value
... 
Поймано моё исключение со значением: 4
>>> raise MyError, 'ой!'
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
__main__.MyError: 'ой!'

В этом примере конструктор по умолчанию __init__() класса Exception был перегружен. Новое поведение отличается лишь созданием нового атрибута value и заменяет поведение по умолчанию, при котором создаётся атрибут args.

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

class Error(Exception):
    """Базовый класс для всех исключений в этом модуле."""
    pass

class InputError(Error):
    """Исключение порождается при ошибках при вводе.

    Атрибуты:
        expression -- выражение на вводе, в котором обнаружена ошибка
        message -- описание ошибки
    """

    def __init__(self, expression, message):
        self.expression = expression
        self.message = message

class TransitionError(Error):
    """Порождается, когда операция пытается выполнить неразрешённый переход
    из одного состояния в другое.

    Attributes:
        previous -- состояние в начале перехода
        next -- новое состояние, попытка принять которое была принята 
        message -- описание, по какой причине такой переход невозможен
    """

    def __init__(self, previous, next, message):
        self.previous = previous
        self.next = next
        self.message = message

Большинство исключений определяются именем, заканчивающимся на «Error», сходно именованию стандартных исключений.

Много стандартных модулей определяют собственные исключения, сообщающие об ошибках, которые могут появится в определяемых ими модулях. Больше информации о классах представлено в главе 1.10, Классы

Определение действий при подчистке

[править]

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

>>> try:
...     raise KeyboardInterrupt
... finally:
...     print 'Прощай, мир!'
... 
Прощай, мир!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
KeyboardInterrupt

Блок finally исполняется всегда, когда интерпретатор покидает оператор try, независимо — были исключения или нет. Если в блоке try появилось исключение, которое не было обработано в блоке except (или появилось в самих блоках except или else) — оно порождается заново после выполнения блока finally. Также блок finally исполняется «по пути наружу», если какой-либо другой блок оператора try был покинут за счёт одного из операторов: break, continue или return. Более сложный пример:

>>> def divide(x, y):
...     try:
...         result = x / y
...     except ZeroDivisionError:
...         print "деление на ноль!"
...     else:
...         print "результат: ", result
...     finally:
...         print "выполнение блока finally"
...
>>> divide(2, 1)
результат:  2
выполнение блока finally
>>> divide(2, 0)
деление на ноль!
выполнение блока finally
>>> divide("2", "1")
выполнение блока finally
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "<stdin>", line 3, in divide
TypeError: unsupported operand type(s) for /: 'str' and 'str'

Как видите, блок finally выполняется при любом событии. Ошибка TypeError порождается при делении двух строк и не перехватывается блоком except, и поэтому порождается заново сразу после выполнения блока finally.

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

Предопределённые действия по подчистке

[править]

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

for line in open("myfile.txt"):
    print(line)

Проблема этого кода в том, что он оставляет файл открытым на неопределённое время после выполнения данной части кода. В простых сценариях это не является проблемой, но может стать ей в больших приложениях. Оператор with позволяет использовать объекты (такие как, например, файлы) таким образом, чтобы вы всегда могли быть уверены в том, что ресурсы будут сразу и корректно очищены.

with open("myfile.txt") as f:
    for line in f:
        print(line)

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

Классы

[править]

За счёт механизма классов Python в язык с минимальным использованием нового синтаксиса и семантики добавляется возможность создания классов. Это смесь классовых механизмов, заимствованных из C++ и Modula-3. Как и в случае модулей, классы в Python не устанавливают абсолютного барьера между определением и программистом, рассчитывая больше на аккуратность и вежливость последнего — чтобы он не «врывался в определения». Наиболее важные возможности классов, тем не менее, содержат в себе всю возможную мощь: механизм наследования классов поддерживает несколько предков для класса, производный класс может перегружать любые методы своего предка или предков, а любой его метод может вызвать метод предка с таким же именем. Объекты могут содержать произвольное количество закрытых (private) данных.

В терминологии C++, все члены класса (включая данные-члены) открыты (public), а все функции-члены — виртуальны. Нет специальных конструкторов и деструкторов. Как в Modula-3, нет краткой ссылки на члены объекта из его методов: функция-метод определяется с явным первым аргументом, описывающем объект, который неявно передаётся при вызове. Как в Smalltalk, классы сами по себе являются объектами, хотя и в более широком смысле: в Python все типы данных — объекты. Таким образом обеспечивается семантика для импортирования и переименования. В отличие от C++ и Modula-3 встроенные типы могут использоваться в качестве предков для расширения возможностей пользователем. Кроме того, как в C++, но не как в Modula-3, большинство встроенных операторов со специальным синтаксисом (арифметические операторы, индексирование и т.д.) могут быть переопределены для экземпляров классов.

Пара слов о терминологии

[править]

Обходя стороной поддерживаемую всем миром терминологию, применимую к разговорам о классах, в нашем случае я буду говорить в терминах C++ и Smalltalk. (Предпочёл бы использовать термины языка Modula-3, поскольку его объектно-ориентированная семантика ближе к ней же в Python, чем к ней же в C++, но предполагаю, что немногие читатели слышали о нём.)

Объекты обладают индивидуальностью, и с одним объектом может быть связано несколько имён (в нескольких областях видимости). Такая практика в других языках известна как совмещение имён (aliasing). Её часто не замечают при первых взглядах на Python, и её можно без последствий игнорировать при работе с основными неизменяемыми типами (числами, строками, кортежами). Тем не менее, совмещение имён имеет (ожидаемый!) эффект на семантике программного кода Python, работающего с изменяемыми объектами — такими как списки, словари и большинство типов, описывающих сущности вне программы (файлы, окна и т.п.). Обычно такая практика считается выгодной, поскольку псевдонимы работают сходно указателям и вероятно даже превосходят их возможности. Например, передача объекта не стоит дорого, поскольку по реализации передаётся только указатель, и если функция изменяет переданный в качестве аргумента объект — переменные в объекте останутся видны и в месте вызова — за счёт этого пропадает необходимость в двух различных механизмах передачи аргументов, как в Pascal.

Области видимости и пространства имён в Python

[править]

Прежде, чем я смогу описать введение в классы, мне нужно поведать вам кое-что о правилах областей видимости в Python. Определения классов проделывают над пространствами имён некоторые ловкие трюки, и поэтому вам нужно знать о принципах работы областей видимости и пространств имён — для того, чтобы полностью понимать происходящее. Кстати, знания по этому предмету полезны любому профессиональному программисту на Python.

Давайте начнём с нескольких определений.

Пространство имён (namespace) — это набор связей имён с объектами[52]. В настоящий момент большинство пространств имён реализованы в виде словарей Python, но не стоит заострять на этом внимание (если только по поводу производительности) — возможно, в будущем реализация изменится. Примеры пространств имён: набор встроенных имён (функции вроде abs() и имён встроенных исключений); глобальные имена в модуле; локальные имена при вызове функции. Важная вещь по поводу пространств имён, которую необходимо знать, — это то, что нет абсолютно никакой связи между именами в разных пространствах имён: например, два разных модуля могут без смущений определять функцию «maximize» оба — пользователи модулей будут использовать имена модулей в качестве префиксов.

Кстати, слово атрибут (attribute) я применяю к любому имени, следующему за точкой, — например, в выражении z.real, real — это атрибут объекта z. Строго говоря, ссылки на имена в модуле являются ссылками на атрибуты: в выражении имя_модуля.имя_функции под имя_модуля скрывается объект модуля, а под имя_функции — его атрибут. В таком случае обнаруживается прямолинейная связь между атрибутами модуля и глобальными именами, определёнными в модуле: они разделяют между собой одно и тоже пространство имён[53].

Запись в атрибуты может быть запрещена (атрибут только для чтения, read-only attribute) или разрешена (перезаписываемый атрибут, writable attribute). В последнем случае присваивание атрибуту является возможным. Атрибуты модуля перезаписываемы: вы можете написать "modname.the_answer = 42"[54]. Перезаписываемые атрибуты могут также быть удалены оператором del. Например, код "del modname.the_answer" удалит атрибут the_answer из объекта с именем modname.

Пространства имён создаются в различные моменты и имеют разные жизненные циклы. Пространство имён, содержащее встроенные имена, создаётся при запуске интерпретатора и не удаляется никогда. Глобальное пространство имён модуля создаётся при вычитке определения модуля; обычно пространства имён модулей также «живут» до выхода из интерпретатора. Выражения, выполняемые верхне-уровневым порождением интерпретатора, прочитанные из файла сценария или интерактивно, рассматриваются как часть модуля под названием __main__, поэтому у них есть своё собственное глобальное пространство имён. (Встроенные имена по факту также живут в модуле, он называется __builtin__).

Локальное пространство имён функции создаётся при её вызове и удаляется, когда функция возвращает значение либо порождает исключение, внутри неё не перехваченное. (На самом деле, лучшим способом объяснить, что происходит на самом деле, было бы «забывание»). Конечно же, рекурсивные порождения имеют свои пространства имён каждое.

Область видимости (scope) — это текстовая территория в программе на Python, на которой прямым образом доступно пространство имён. «Доступно прямым образом» подразумевает, что неопознанная ссылка на имя вынуждает интерпретатор искать это имя в пространстве имён.

Несмотря на то, что области видимости определяются статически, используются они динамически. В любой момент во время выполнения существует как минимум три вложенных области видимости, чьи пространства имён доступны прямым образом: самая глубокая[55] область видимости, по которой поиск осуществляется в первую очередь, содержит локальные имена; пространства имён всех заключающих [данный код] функций, поиск по которым осуществляется начиная с ближайшей заключающей [код] области видимости; область видимости среднего уровня, по ней следующей проходит поиск и она содержит глобальные имена текущего модуля; и самая внешняя область видимости (заключительный поиск) — это пространство имён, содержащее встроенные имена.

Если имя определяется глобально, тогда все ссылки и присваивания уходят прямо в область видимости среднего уровня, содержащую глобальные имена модуля. Иначе все переменные, найденные вне самой глубокой области видимости, являются переменными только для чтения (попытка записать значение в такую переменную создаст новую локальную переменную в самой глубокой области видимости, оставляя идентично названную вовне переменную без изменений).

Обычно локальная область видимости ссылается на локальные имена текущей (на уровне текста) функции. Вне функций локальная область видимости ссылается на то же пространство имён, что и глобальная область видимости: пространство имён модуля. Определения классов помещают в локальную область видимости ещё одно пространство имён.

Важно осознавать, что области видимости ограничиваются на текстовом уровне: глобальная область видимости функции, определённая в модуле, является пространством имён этого модуля, независимо от того, откуда или по какому псевдониму была эта функция вызвана. С другой стороны, фактический поиск имён осуществляется динамически, во время выполнения — как бы то ни было, описание языка эволюционирует в направлении статического разрешения имён, так что не стоит полагаться на динамическое разрешение имён. (По факту, локальные переменные уже определены статично.)

Особая хитрость в Python состоит в том, что — при условии, что в данной области не включены операторы global, — присваивания именам всегда уходят в самую глубокую область видимости. Присваивания не копируют данных — они лишь связывают имена с объектами. То же самое верно и для удалений: оператор "del x" удаляет связь x из пространства имён, на которое ссылается локальная область видимости. В действительности, все операции, вводящие новые имена, используют локальную область видимости: в частности, операторы импорта и описаний функций связывают имя модуля или функции в локальной области видимости соответственно. (Для того, чтобы указать определённой переменной, что она должна быть расположена в глобальной области видимости, может использоваться оператор global.)

Первый взгляд на классы

[править]

В описании классов представлено немного нового синтаксиса, три новых типа объектов[56] и некоторое количество новой семантики.

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

[править]

Простейшая форма определения класса выглядит так:

class ИмяКласса:
    <выражение-1>
    .
    .
    .
    <выражение-N>

Определения классов, как и определения функций (операторы def) должны быть исполнены для того, чтобы иметь какой-либо эффект. (Вы можете, предположим, поместить определение класса в ветку оператора if или внутрь функции.)

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

При вводе определения класса создаётся новое пространство имён, и используется в качестве локальной области видимости — таким образом, все присваивания локальным переменным уходят в это новое пространство имён. В частности, определения функций связываются здесь с именами новых функций.

При удачном окончании парсинга определения класса (при достижении конца определения), создаётся объект-класс (class object). По существу, это обёртка вокруг содержимого пространства имён, созданного по определению класса; подробнее объекты классов мы изучим в следующем разделе. Оригинальная локальная область видимости (та, которая действовала в последний момент перед вводом определения класса) восстанавливается, а объект-класс тут же связывается в ней с именем класса, указанном в заголовке определения класса (в примере — ИмяКласса).

Объекты-классы

[править]

Объекты-классы поддерживают два вида операций: ссылки на атрибуты и создание экземпляра.

Ссылки на атрибуты (Attribute references) используют стандартный синтаксис, использующийся для всех ссылок на атрибуты в Python: объект.имя. Корректными именами атрибутов являются все имена, которые находились в пространстве имён класса при создании объекта-класса. Таким образом, если определение класса выглядело так:

class MyClass:
    """Простой пример класса"""
    i = 12345
    def f(self):
        return 'привет мир'

то MyClass.i и MyClass.f являются корректными ссылками на атрибуты, возвращающими целое и объект-функцию (function object) соответственно. Атрибутам класса можно присваивать значение, так что вы можете изменить значение MyClass.i через присваивание. __doc__ также является корректным атрибутом, возвращающим строку документации, принадлежащей классу: "Простой пример класса".

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

x = MyClass()

создаёт новый экземпляр класса и присваивает этот объект локальной переменной x.

Операция создания экземпляра (instantiation, «вызов» объекта-класса) создаёт пустой объект. Большая часть классов предпочитает создавать экземпляры, настроенные с учётом определённого начального состояния. Для этого класс может определять специальный метод под именем __init__(), например так:

def __init__(self):
    self.data = []

Когда в классе определён метод __init__(), при создании экземпляра автоматически вызывается __init__() нового, созданного, класса. Так, в этом примере, новый инициализированный экземпляр может быть получен за счёт выполнения кода:

x = MyClass()

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

>>> class Complex:
...     def __init__(self, realpart, imagpart):
...         self.r = realpart
...         self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)

Объекты-экземпляры

[править]

Теперь, что же мы можем делать с объектами-экземплярами? Единственные операции, доступные объектам-экземплярам, — это ссылки на атрибуты. Есть два типа корректных имён атрибутов — это атрибуты-данные и методы.

Атрибуты-данные (data attributes) аналогичны «переменным экземпляров» в Smalltalk и «членам-данным» в C++. Атрибуты данных не нужно описывать — как и переменные, они начинают существование в момент первого присваивания. Например, если x — экземпляр созданного выше MyClass, следующий отрывок кода выведет значение 16, не вызвав ошибок:

x.counter = 1
while x.counter < 10:
    x.counter = x.counter * 2
print x.counter
del x.counter

Другой тип ссылок на атрибуты экземпляра — это метод (method). Метод — это функция, «принадлежащая» объекту. (В Python термин не уникален для экземпляров класса: другие объекты также могут иметь методы. Например, объекты списков имеют методы append, insert, remove, sort и т. д. Тем не менее, далее по обсуждению под термином «метод» мы будем понимать только методы объектов-экземпляров классов, пока отдельно не будет указано иное.)

Корректные имена методов объектов-экземпляров зависят от их класса. По определению, все атрибуты класса, являющиеся объектами- функциями, описывают соответствующие методы его экземпляров. Так, в нашем примере, x.f является корректной ссылкой на метод, а x.i ей не является, поскольку не является и MyClass.i. Но при этом x.f не то же самое, что MyClass.f — это объект-метод, а не объект-функция.

Объекты-методы

[править]

Обычно, метод вызывают сразу после его связывания [с функцией]:

x.f()

На примере MyClass такой код возвратит строку 'привет мир'. Однако, не обязательно вызывать метод так уж сразу: x.f — это объект-метод, он может быть отложен и вызван когда-либо позже. Например:

xf = x.f
while True:
    print xf()

будет печатать 'привет мир' до конца времён.

Что конкретно происходит при вызове метода? Вы, возможно, заметили, что x.f() выше был вызван без аргументов, хотя в описании функции f аргумент был указан. Что же случилось с аргументом? Несомненно, Python порождает исключение, когда функция, требующая присутствия аргумента, вызвана без единого — даже, если он на самом деле не используется...

Теперь вы, возможно, догадались: отличительная особенность методов состоит в том, что в качестве первого аргумента функции передаётся объект. В нашем примере вызов x.f() полностью эквивалентен вызову MyClass.f(x). В общем случае, вызов метода со списком из n аргументов эквивалентен вызову соответствующей функции со списком аргументов, созданным за счёт вставки объекта, вызвавшего метод, перед первым аргументом.

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

Различные замечания

[править]

Атрибуты-данные переопределяют атрибуты-методы с тем же именем; для того, чтобы обезопасить себя от случайных конфликтов имён, которые могут привести к трудно-обнаруживаемым ошибкам в больших программах, разумно использовать какое-нибудь соглашение, которое могло бы уменьшить шансы возникновения конфликтов. Возможные соглашения включают в себя: написание имён методов строчными буквами, предварение имени атрибутов-данных некоторой короткой уникальной строкой (предположим, лишь символом подчёркивания ("_")), или использование глаголов для именования методов и существительных для именования данных.

Методы могут ссылаться на атрибуты-данные также, как и обычные пользователи («клиенты») объекта. Другими словами, использование классов не окажется удобным для разработки глубоко абстрактных типов данных. По факту, в Python нет ничего, вынуждающего вас скрывать данные — всё это основано на соглашениях. (С другой стороны, реализация Python, написанная на C, может полностью скрывать детали разработки и, если нужно, контролировать доступ к объекту; это можно использовать в расширениях для Python, написанных на C.)

Клиенты должны использовать атрибуты-данные с осторожностью — они могут нарушить некоторые неизменные положения, на которые опираются методы, за счёт изменения используемых ими атрибутов-данных. Заметьте, что клиенты могут добавлять собственные атрибуты-данные к объектам-экземплярам, не нарушая работы методов, поскольку конфликты имён исключаются — опять же, соглашение об именовании может избавить вас от головной боли и в этих случаях.

У методов нет краткой записи для ссылок изнутри на атрибуты-данные (и другие методы!). Я нахожу, что это и вправду повышает читабельность методов: нет шанса спутать локальные переменные и переменные экземпляров при просмотре тела метода.

Обычно, первый аргумент метода называется self. Это не более чем соглашение: имя self не имеет абсолютно никакого специального смысла для языка Python. (Однако, обратите внимание, что если вы не следуете соглашениям, ваш код может стать менее читабелен для других программистов; и также, потенциально, программа навигации по классам может опираться на такие соглашения.)

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

# Функция, определённая вне класса
def f1(self, x, y):
    return min(x, x+y)

class C:
    f = f1
    def g(self):
        return 'привет мир'
    h = g

Теперь f, g и h — все являются атрибутами класса C, ссылающимися на объекты-функции, и, следовательно, все они являются методами экземпляров Ch становится полностью эквивалентен g. Заметьте, что такая практика обычно служит лишь тому, чтобы запутать читателя программы.

Методы могут вызывать другие методы за счёт использования атрибутов-методов аргумента self:

class Bag:
    def __init__(self):
        self.data = []
    def add(self, x):
        self.data.append(x)
    def addtwice(self, x):
        self.add(x)
        self.add(x)

Методы могут ссылаться на глобальные имена таким же образом, как и обычные функции. Глобальная область видимости, связанная с методом, — это модуль, содержащий определение класса. (Сам класс никогда не используется в качестве глобальной области видимости!) В то время, как одни редко находят причины для использования глобальных данных в методах, существует множество вполне законных способов использования глобальной области видимости: для примера, функции и модули, импортированные в глобальную область видимости, могут использоваться методами так же хорошо, как функциями и классами, в них определёнными. Обычно класс, содержащий метод, сам определён в этой глобальной области видимости, и в следующем разделе мы найдём пару хороших причин, почему метод может иметь желание ссылаться на собственный класс!

Наследование

[править]

Конечно же, не поддерживай «класс» наследование, не стоило бы называть его «классом». Синтаксис производного класса выглядит так:

class ИмяПроизводногоКласса(ИмяБазовогоКласса):
    <выражение-1>
    .
    .
    .
    <выражение-N>

Имя ИмяБазовогоКласса должно быть определено в области видимости, содержащей определение производного класса. Вместо имени базового класса также позволяется использовать другие выражения. Это может быть полезно, например, когда базовый класс определён в другом модуле:

class ИмяПроизводногоКласса(имямодуля.ИмяБазовогоКласса):

Использование определения производного класса проходит таким же образом, как и базового. Базовый класс полностью сохраняется по завершению конструирования объекта-класса. Такой метод используется для разрешения ссылок на атрибуты[57]: если запрошенный атрибут не был найден в самом классе, поиск продолжается в базовом классе. Правило применяется рекурсивно, если базовый класс сам является производным от некоторого другого класса.

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

Производные классы могут перегружать методы своих базовых классов. Поскольку у методов нет особых привилегий при вызове других методов того же объекта, метод базового класса, вызывающий другой метод, определённый в этом же классе, может закончить на вызове метода производного класса, его перегружающего. (Для программистов на C++: все методы в Python фактически виртуальны.)

При перегрузке метода в производном классе возможна не только замена действия метода базового класса с тем же именем, но и его расширение. Существует простой способ вызвать метод базового класса прямым образом: просто вызовите "ИмяБазовогоКласса.имяметода(self, аргументы)". Такой способ будет неожиданно полезным и для клиентов. (Обратите внимание, что он работает только если базовый класс определён и импортирован прямо в глобальную область видимости.)

В языке Python есть две функции, которые работают с наследованием:

  • Используйте isinstance() чтобы проверить тип объекта: isinstance(obj, int) возвратит True только если obj.__class__ является int или некоторым классом, наследованным от int.
  • Используйте issubclass() чтобы проверить наследственность класса: issubclass(bool, int) возвратит True, поскольку класс bool является наследником (subclass) int. Однако, issubclass(unicode, str) возвратит False, поскольку класс unicode не является наследником str (у них есть только общий дочерний класс, basestring).

Множественное наследование

[править]

Python также поддерживает ограниченную форму множественного наследования (multiple inheritance). Определение класса с несколькими базовыми классами будет выглядеть так:

class ИмяПроизводногоКласса(Базовый1, Базовый2, Базовый3):
    <выражение-1>
    .
    .
    .
    <выражение-N>

Для классов в старом стиле единственное правило — это правило «глубина-сначала», затем «слева-направо». Таким образом, если атрибут не найден в ИмяПроизводногоКласса, его поиск выполняется в Базовом1, затем (рекурсивно) в базовых классах Базового1 и только если он там не найден, поиск перейдёт в Базовый2 и так далее.

(Для широкого круга людей, на первый взгляд, поиск в классах Базовый2 и Базовый3 перед поиском в базовых классах Базового1, выглядит более естественным. Однако, предварительно здесь необходимо знание о том, определён ли действительно атрибут в Базовом1, или он определён в одном из его предков — иначе вы столкнётесь с конфликтом имён с атрибутом в классе Базовый2. Правило «глубина-сначала» не делает различий между прямыми и наследованными атрибутами Базового1.)

Для классов в новом стиле, порядок разрешения методов[58] (method resolution order) меняется динамически, чтобы обеспечить возможность совместных вызовов super(). Этот способ известен в некоторых других языках с поддержкой множественного наследования как "вызов-следующего-метода" („call-next-method“) и имеет больше возможностей, чем вызов родительского метода в языках с единичным наследованием.

В случае классов в новом стиле, динамическое упорядочивание (dynamic ordering) имеет большую важность, поскольку все вариации множественного наследования проявляют в себе эффект ромбовых отношений (когда как минимум один родительский класс может быть доступен различными путями из низшего в иерархии класса). Например, все классы нового стиля наследуются от object, так что множественное наследование в любом виде предоставляет более одного пути для того, чтобы достичь object. Чтобы защитить базовые классы от двойных и более запросов, динамический алгоритм «выпрямляет» (linearizes) порядок поиска таким способом, что тот сохраняет указанный слева-направо порядок для каждого класса, который вызывает каждый родительский класс только единожды и является монотонным (значит, класс можно сделать наследником, не взаимодействуя с порядком предшествования его родителей). Обобщённые вместе, эти свойства позволяют разрабатывать надёжные и расширяемые классы, используя множественное наследование. С подробностями можно ознакомиться по этой ссылке: http://www.python.org/download/releases/2.3/mro/ (перевод).

Приватные переменные

[править]

Присутствует ограниченная поддержка идентификаторов, приватных для класса. Любой идентификатор в форме __spam (как минимум два предшествующих символа подчёркивания, как максимум один завершающий) заменяется дословно на _classname__spam, где classname — текущее имя класса, лишённое предшествующих символов подчёркивания. Это искажение (mangling) производится без оглядки на синтаксическую позицию идентификатора, поэтому может использоваться для определения переменных, приватных для класса экземпляров, переменных класса, методов, переменных в глобальной области видимости (globals), и даже переменных, использующихся в экземплярах, приватных для этого класса на основе экземпляров других классов. Имя может быть обрезано, если его длина превышает 255 символов. Вне классов, или когда имя состоит из одних символов подчёркивания, искажения не происходит.

Предназначение искажения имён состоит в том, чтобы дать классам лёгкую возможность определить «приватные» переменные экземпляров и методы, не беспокоясь о переменных экземпляров, определённых в производных классах, и о забивании кодом вне класса переменных экземпляров. Обратите внимание, что правила искажения имён разработаны, в основном, чтобы исключить неприятные случайности — решительная душа всё ещё может получить доступ или изменить переменные, предполагавшиеся приватными. В некотором особом окружении, таком как отладчик, это может оказаться полезным — и это единственная причина, по которой лазейка не закрыта. (Внимание: наследование класса с таким же именем как и у базового делает возможным использование приватных переменных базового класса.)

Заметьте, что код, переданный в exec или eval() или execfile() не предполагает в качестве текущего имени класса имя класса, порождающего вызов — так же, как и в случае эффекта с оператором global — эффекта, который также ограничен для всего побайтно-компилирующегося кода. И, такое же ограничение применимо для функций getattr(), setattr() и delattr(), и также для прямой ссылки на __dict__.

Всякая всячина

[править]

Иногда полезно иметь тип данных, похожий на record из языка Pascal или struct из языка C — для сборки вместе нескольких поименованных элементов, содержащих данные. Пустое определение класса хорошо поможет[59]:

class Employee:
    pass

john = Employee() # Создать пустую запись о рабочем

# Заполнить поля записи
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

Отрезок кода на Python, требующий на входе определённого абстрактного типа данных, часто может быть передан классу, который эмулирует вместо него методы этого типа данных. Например, если у вас есть функция, форматирующая некоторые данные из объекта файла, то вы можете определить методы класса read() и readline(), которые вместо этого получат данные из строкового буфера и передать им его в качестве аргумента.

Объекты-методы экземпляров также имеют атрибуты: m.im_self — объект-экземпляр с методом m(), а m.im_func — объект-функция, соответствующий методу.

Исключения — тоже классы

[править]

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

Вот две новых (синтаксически) корректных формы оператора raise:

raise Класс, экземпляр

raise экземпляр

В первой форме, экземпляр должен быть экземпляром Класса, или класса, производного от него. Вторая форма является краткой записью следующего кода:

raise экземпляр.__class__, экземпляр

Класс в блоке except является сопоставимым с исключением, если является этим же классом или самим по себе базовым классом (никаких других способов обхода — описанный в блоке except производный класс не сопоставим с базовым). Например, следующий код выведет B, C, D в этом порядке:

class B:
    pass
class C(B):
    pass
class D(C):
    pass

for c in [B, C, D]:
    try:
        raise c()
    except D:
        print "D"
    except C:
        print "C"
    except B:
        print "B"

Обратите внимание, что если бы блоки except шли в обратном порядке (начиная с "except B"), код вывел бы B, B, B — сработал бы первый совпадающий блок except.

При выводе сообщения об ошибке о необработанном исключении, выводится класс исключения, затем двоеточие и пробел, и наконец экземпляр, приведённый к строке за счёт встроенной функции str().

Итераторы

[править]

К этому моменту вы, возможно, заметили, что используя оператор for можно пройти циклом по большинству объектов-контейнеров:

for element in [1, 2, 3]:
    print element
for element in (1, 2, 3):
    print element
for key in {'один':1, 'два':2}:
    print key
for char in "123":
    print char
for line in open("myfile.txt"):
    print line

Такой стиль доступа к элементам прост, лаконичен и удобен. Использованием итераторов (iterators) пропитан язык Python, и это его выделяет среди других. Негласно, оператор for вызывает метод iter() объекта-контейнера. Функция возвращает объект итератора, который определяет метод next(), который по очереди получает доступ к элементам в контейнере, по одному за раз. Если больше не остаётся элементов, метод next() порождает исключение StopIteration, которое сообщает оператору for о необходимости завершения прохода. Следующий пример показывает как это всё работает:

>>> s = 'абв'
>>> it = iter(s)
>>> it
<iterator object at 0x00A1DB50>
>>> it.next()
'а'
>>> it.next()
'б'
>>> it.next()
'в'
>>> it.next()

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
    it.next()
StopIteration

Ознакомившись с механизмами, скрытыми за протоколом итераторов, легко добавить возможность итерирования к вашим классам. Определите метод __iter__(), который возвращает объект с методом next(). Если класс определяет и метод next(), тогда __iter__() может просто возвращать self.

class Reverse:
    "Итератор для прохождения циклом по последовательности в обратном направлении"
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    def __iter__(self):
        return self
    def next(self):
        if self.index == 0:
            raise StopIteration
        self.index = self.index - 1
        return self.data[self.index]

>>> for char in Reverse('спам'):
...     print char
...
м
а
п
с

Генераторы

[править]

Генераторы (generators) — простой и мощный инструмент для создания итераторов. Они записываются как обычная функция, но где бы им ни было необходимо вернуть данные, используется оператор yield. Каждый раз, при вызове next(), генератор возвращается к месту, где он был оставлен (он запоминает все значения данных, а также какой оператор был выполнен последним). Пример показывает, что создание генераторов может быть тривиально простым:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]
	
>>> for char in reverse('гольф'):
...     print char
...
ф
ь
л
о
г

Всё, что можно сделать с использованием генераторов, может быть сделано с использованием основанных на итераторах классов, как описано в предыдущем разделе. Благодаря автоматическому созданию методов __iter__() и next() генераторы так компактны.

Другая важная особенность состоит в том, что между вызовами сохраняются локальные переменные и состояние выполнения (execution state). Это позволяет конструкциям функций быть проще, а получению переменных экземпляров быть намного легче, нежели с использованием self.index и self.data.

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

Выражения-генераторы

[править]

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

Примеры[60]:

>>> sum(i*i for i in range(10))                 # сумма квадратов
285

>>> xvec = [10, 20, 30]
>>> yvec = [7, 5, 3]
>>> sum(x*y for x,y in zip(xvec, yvec))         # скалярное произведение
260

>>> from math import sin, radians
>>> sine_table = dict((x, sin(radians(x))) for x in range(0, 91))

>>> unique_words = set(word  for line in page  for word in line.split())

>>> valedictorian = max((student.gpa, student.name) for student in graduates)

>>> data = 'golf'
>>> list(data[i] for i in range(len(data)-1,-1,-1))
['f', 'l', 'o', 'g']


Обзор стандартной библиотеки

[править]

Сноски:

  1. (Прим. перев.) Здесь и далее по учебнику понятия пользователя и программиста в некоторые моменты пересекаются, ввиду того что последний рассматривается как «пользователь языка». Обычно это понятно из контекста, но тем не менее, на всякий случай, поясняю.
  2. (Прим. перев.) primary promptзд., основное приглашение: приглашение к вводу команды или нескольких команд (сценария);
  3. (Прим. перев.) continuation linesзд., продолжающие строки (строки продолжения): строки, продолжающие текст команды (оператора, выражения), или раскрывающие внутреннее устройство внешнего оператора (команды, выражения), в последнем случае определяются дополнительным отступом от края — углублением в структуру;
  4. (Прим. перев.) secondary promptзд., вспомогательное приглашение: приглашение к продолжению ввода команды или набора команд (сценария) с использованием продолжающих строк;
  5. (Прим. перев.) Здесь и далее тексты некоторых исходных кодов также будут переведены в примечаниях (вместе с ключевыми словами, поэтому их нельзя будет запустить в интерпретаторе), чтобы показать их смысл или задумку, которые не всегда очевидны без перевода (если он вам нужен :) ):

    >>> мир_плоский = 1
    >>> если мир_плоский:
    ...    вывести "Будьте осторожны — не упадите!"
    ...
    Будьте осторожны — не упадите!

  6. Известная проблема в пакете GNU Readline может этому помешать.
  7. (Прим. перев.) А также стандартные каналы ввода-вывода — не открываются. Это может быть важно, если вы захотите использовать Python-скрипт внутри пакетного файла, так как перенаправление стандартных каналов ввода-вывода работать не будет.
  8. (Прим. перев.):
    hello = "Это довольно длинная строка, содержащая\n\
    несколько строк текста — такой вы бы представили её в C.\n\
       Обратите внимание, что пробельное пространство в начале строки\
     имеет значение."
    
    print hello
    
  9. (Прим. перев.) raw string — для описания сырой строки (не требующей последующей обработки) используется префикс r.
  10. (Прим. перев.): Supercalifragilisticexpialidocious — английское слово из одноименной песни, прозвучавшей в фильме Мэри Поппинс.
  11. Часто записывается как "i18n" — internationalization = i + 18 символов + n
  12. (Прим. перев.) UTF-16 — либо в двух либо в четырёх байтах, UTF-8 — от одного до четырёх байт
  13. (Прим. перев.) Размер отступа для блока не унифицирован - табуляция может чередоваться с пробелами, это может быть один пробел или же пять — главное, чтобы у всего блока он был одинаковым и был больше по величине чем у внешнего (обрамляющего) блока. Тем не менее, чаще всего программисты на Python устанавливают в редакторах режим замены табуляции четырьмя пробелами и при наборе кода вложенность блока обозначают отступом с соответствующим количеством нажатий клавиш табуляции. Благодаря этому код остается читабельным во всех текстовых редакторах и в большинстве браузеров (или, в редких случаях, требует минимального форматирования для корректного отображения)).
  14. (Прим. перев.) More Control Flow Tools; Control Flow - поток команд, процесс управления, алгоритм; подразделы в оригинале используют множественное число: "Операторы (выражения, инструкции) if, "Операторы (выражения, инструкции) for", и. т. п.
  15. (Прим. перев.) Defenestrate(англ.) — выкидывать кого-либо из окна с целью покалечить. Другие слова — «кошка» и «окно».
  16. (Прим. перев.) Здесь слово «символ» используется не в привычном значении — под символом подразумевается нечто лингвистическое, описывающее какую-либо сущность (близко к значению символизировать), в данном случае: имя переменной — саму переменную.
  17. На самом деле, лучшим определением был бы вызов по ссылке на объект (call by object reference), так как при передаче изменяемого объекта, вызывающему будут видны все изменения, которые производит над объектом вызываемый (вставка элементов в список)
  18. (Прим. перев.):

    опред спросить_подтверждения(вопрос, попытки=4, протест='Да или нет, пожалуйста!'):
        пока Истина:
            ответ = прямой_ввод(вопрос)
            если ответ среди ('д', 'да', 'ага'): вернуть Истина
            если ответ среди ('н', 'не', 'нет', 'не-а'): вернуть Ложь
            попытки = попытки - 1
            если попытки < 0: породить ОшибкаВВ, 'пользователь-отказник'
            вывести протест


    (Refusenik(англ.)Отказник). Ниже: «...спросить_подтверждения('Вы действительно хотите выйти?') или так: спросить_подтверждения('Согласны ли вы перезаписать файл?', 2)...»
  19. (Прим. перев.) В оригинале — Keyword Arguments — аргументы по ключевым словам, ключевые аргументы (далее используется слово keywords для набора имён параметров непостоянного количества. Ключевые - возможно, корректнее, но как мне показалось — менее понятно (альтернативный перевод))
  20. (Прим. перев.):

    опред попугай(вольтаж, состояние='труп', действие='ввум', тип='Норвежский Голубой'):
        вывести "-- Этот попугай не сделает ", действие,
        вывести "если вы пропустите через него ", вольтаж, " вольт."
        вывести "-- Великолепное оперенье, ", тип
        вывести "-- Он", состояние, "!"


    (Из скетча Монти Пайтона)
  21. (Прим. перев.):

    попугай(действие = 'ВВУУУУМ', вольтаж = 1000000)
    попугай('тысячу', состояние = 'слёг в могилу')
    попугай('миллион', 'лишённый жизни', 'прыжок')


  22. (Прим. перев.):

    попугай()
    попугай(вольтаж=5.0, 'мёртв')
    попугай(исполнитель='Джон Клиз')


  23. (Прим. перев.) В главе о функциях автор отталкивается не от привычного по другим учебникам первоначального полного описания определения функций, а уже потом — описания их вызова, а объединяет обе темы вместе (как минимум в случае с непостоянным списком аргументов это возможно непривычно, но оправдано), отдавая приоритет описанию вызова. Учебник рассчитан на тех, кто уже программирует на каком-либо языке программирования и потенциально не должен смутить читателя.
  24. (Прим. перев.):

    опред лавка_сыров(сорт, *аргументы, **именованные):
        вывести "-- Есть ли у вас ", сорт, "?"
        вывести "-- Простите, весь ", сорт, " у нас закончился."
        для аргумент среди аргументы: вывести аргумент
        вывести "-"*40
        имена = именованные.ключи()
        имена.сортировка()
        для имя среди имена: вывести имя , ":", именованные[имя]


    (Из скетча Монти Пайтона)
  25. (Прим. перев.):

    лавка_сыров("Лимбургер", "Это очень расстраивает, сэр.",
            "Это действительно очень, ОЧЕНЬ расстраивает, сэр.",
            торговец="Майкл Палин",
            клиент="Джон Клиз",
            скетч="Скетч о сырной лавке")

  26. (Прим. перев.):

    -- Есть ли у вас Лимбургер ?
    -- Простите, весь Лимбургер у нас закончился.
    Это очень расстраивает, сэр.
    Это действительно очень, ОЧЕНЬ расстраивает, сэр.
    ----------------------------------------
    клиент: Джон Клиз
    торговец : Майкл Палин
    скетч : Скетч о сырной лавке


  27. (Прим. перев.):

    опред попугай(вольтаж, состояние='труп', действие='ввум', тип='Норвежский Голубой'):
    ...    вывести "-- Этот попугай не сделает ", действие,
    ...    вывести "если вы пропустите через него ", вольтаж, " вольт."
    ...    вывести "-- Это", состояние, "!"
    ...
    >>> d = {"вольтаж": "четыре миллиона", "состояние": "кровавая кончина", "действие": "ВВУМ"}
    >>> попугай(**d)
    -- Этот попугай не сделает ВВУМ если вы пропустите через него четыре миллиона вольт. Это кровавая смерть !


    (Из скетча Монти Пайтона)
  28. (Прим. перев.)PEP (Python Enchancement Proprosal, Предложение по улучшению Python) — документ, имеющий стандартизированный формат и описывающий какую-либо из возможностей или какое-либо из свойств языка Python, планируемые для разработки в будущем (или уже разработанную). В завершённом варианте используется как основное соглашение по данной функциональности. (см. также пояснение на странице языка Python)
  29. (Прим. перев.) CamelCase (ВерблюжийСтиль) — стиль именования идентификаторов, при котором все слова, записанные строчными буквами, объединяются в одно и первая буква каждого слова выделяется заглавной буквой — такая запись напоминает верблюжьи горбы, в связи с чем и названа.
  30. (Прим. перев.) Пусть данный пример по вине переводчика и использует русские буквы, соглашение корректно настаивает на использовании латиницы для именования идентификаторов, см. следующий пункт соглашения
  31. (Прим. перев.) В строковых литералах (не комментариях) это, конечно же, позволено
  32. (Прим. перев.) Не кодировка, а именно набор символов — латиница, пунктуация и несколько служебных символов. Кодировка по умолчанию для файлов с исходными кодами, начиная с Python 3.0, всегда UTF-8 — дабы существовала возможность использовать национальные символы в строковых литералах и, в редких случаях — комментариях.
  33. (Прим. перев.) Lists Comprehensions, comprehension - включение, вложение, добавление (понимание, охват, интенция).
  34. (Прим. перев.) в Python, как и в большинстве других языков программирования, используется европейский стандарт разделения целой и дробной части десятичных дробей точкой.
  35. (Прим. перев.):

    >>> корзина = ['яблоко', 'апельсин', 'яблоко', 'груша', 'апельсин', 'банан']
    >>> фрукты = набор(корзина)    # создать множество
    >>> фрукты
    набор(['апельсин', 'груша', 'яблоко', 'банан'])
    >>> 'апельсин' среди фрукты    # быстрая проверка вхождения
    Истина
    >>> 'росичка' среди фрукты
    Ложь


  36. (Прим. перев.):

    >>> вопросы = ['имя', 'задание', 'любимый цвет']
    >>> ответы = ['ланцелот', 'святой грааль', 'синий']
    >>> для в, о среди зип(вопросы, ответы):
    ...     вывести 'Каков(о) ваш(е) {0}? {1}.'.форматировать(в, о)
    ...
    Каков(о) ваш(е) имя? ланцелот.
    Каков(о) ваш(е) задание? святой грааль.
    Каков(о) ваш(е) любимый цвет? синий.
  37. (Прим. перев.) short-circuit(англ.) — закоротить, обходить, идти обходными путями (для достижения цели), препятствовать
  38. Не стоит полагаться на правила сравнения разных типов — в последующих версиях языка они могут быть изменены.
  39. Фактически, определения функций — также „исполняемые“ „выражения“. Выполнение помещает имя функции в глобальную таблицу символов.
  40. (Прим. перев.) подмодуль (submodule), зд. — модуль, находящийся в пакете. подпакет (subpackage), зд. — пакет, расположенный в пакете.
  41. (Прим. перев.) representation (англ.) - представление, образ, форма
  42. (Прим. перев.) We are the knights who say "Ni!" (Мы - те рыцари, что говорят "Ни!") — цитата из сцены 13-й фильма Монти Пайтон и Святой Грааль (Monty Python and Holy Grail)
  43. (Прим. перев.) вывести('Этот {пища} — {прилагательное}.'.format(пища='фарш', прилагательное='непередаваемо ужасен'))
  44. (Прим. перев.) вывести 'История о {0}, {1}, и {другой}.'.форматировать('Билл', 'Манфред', другой='Георг')
  45. (Прим. перев.) В качестве десятичного разделителя в Python используется точка, в соответствии со стандартами большинства стран.
  46. (Прим. перев.) В источнике, в большинстве случаев, разделяется понятие строки как типа данных и строки как строки текста (ср., строчка) за счёт слов string и line соответственно. В русском языке это различие передать сложно, тем не менее я старался по возможности избежать двусмысленностей.
  47. (Прим. перев.) Возможно, подразумевается ответ на главный вопрос жизни, вселенной и всего такого
  48. (Прим. перев.) Консервирование в Python можно рассматривать как частный случай сериализации; ещё про консервирование можно почитать здесь.
  49. (Прим. перев.) Parser (англ.) — программа синтаксического анализа, синтаксический анализатор; программа грамматического разбора
  50. (Прим. перев.) Token (англ.) — некоторая смысловая единица выражения: оператор, ключевое слово, значение переменной, ...
  51. (Прим. перев.) Handle (англ.)зд., [ловить и] обрабатывать, управлять, контролировать, разбирать, следить, управляться с...
  52. (Прим. перев.) A namespace is a mapping from names to objects (англ.)(букв.) пространство имён суть отображение имён на объекты
  53. Исключая одну тонкость. У объектов модулей есть скрытый, только для чтения, атрибут под именем __dict__, возвращающий словарь, использовавшийся для формирования пространства имён модуля; имя __dict__ является атрибутом, но не глобальным именем. Очевидно, что всё это нарушает абстракцию реализации пространства имён и его использование следует ограничить вещами вроде посмертных отладчиков
  54. (Прим. перев.) Снова ответ на главный вопрос жизни, вселенной и всего такого
  55. (Прим. перев.) Innermost scope, outermost scope (англ.) — самая внутренняя и самая внешняя области видимости
  56. (Прим. перев.) Class Objects, Instance Objects, Method Objects (англ.) — объекты классов, объекты экземпляров, объекты методов; по тексту перевода, хоть и могут восприниматься непривычно, используются термины «объекты-классы», «объекты-экземпляры» и «объекты-методы» — ввиду того, что словосочетание «объект класса» может обозначать и принадлежность объекта классу
  57. (Прим. перев.) Resolving attribute references (англ.) — разрешение (поиск значений) ссылок на атрибуты.
  58. (Прим. перев.) другими словами — поиска корректной привязки для метода
  59. (Прим. перев.):
    class Рабочий:
        pass
    
    джон = Рабочий() # Создать пустую запись о рабочем
    
    # Заполнить поля записи
    джон.имя = 'Джон До'
    джон.отдел = 'компьютерная лаборатория'
    джон.зарплата = 1000
    
  60. (Прим. перев.):

    >>> сумма(i*i для i в диапазон(10)) # сумма квадратов
    285

    >>> вектор_а = [10, 20, 30]
    >>> вектор_б = [7, 5, 3]
    >>> сумма(x*y для x,y в зип(вектор_а, вектор_б)) # скалярное произведение
    260

    >>> из матем импорт пи, синус
    >>> таблица_синусов = словарь((x, синус(x*пи/180)) для x в диапазон(0, 91))

    >>> уникальные_слова = набор(слово  для строка в страница  для слово в строка.разбить())

    >>> выпускники = макс((студент.ср_балл, студент.имя) для студент в окончившие)

    >>> данные = 'гольф'
    >>> список(данные[i] для i в диапазон(длина(данные)-1,-1,-1))
    ['ф', 'ь', 'л', 'о', 'г']