Версия для печати

Архив документации на OpenNet.ru / Раздел "Python" (Многостраничная версия)

В глубь языка Python

В глубь языка Python

19 марта 2002

Оригинал этой книги опубликовон по адресу http://diveintopython.org/, перевод на русский язык — по адресу http://diveinto.python.ru/. Если вы читаете ее в каком-либо другом месте, то, возможно, вы имеете дело не с самой последней версией.

Permission is granted to copy, distribute, and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in GNU Free Documentation License.

The example programs in this book are free software; you can redistribute and/or modify them under the terms of the Python license as published by the Python Software Foundation. A copy of the license is included in Python 2.1.1 license.

Содержание


В глубь языка Python

19 марта 2002

Оригинал этой книги опубликовон по адресу http://diveintopython.org/, перевод на русский язык — по адресу http://diveinto.python.ru/. Если вы читаете ее в каком-либо другом месте, то, возможно, вы имеете дело не с самой последней версией.

Permission is granted to copy, distribute, and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in GNU Free Documentation License.

The example programs in this book are free software; you can redistribute and/or modify them under the terms of the Python license as published by the Python Software Foundation. A copy of the license is included in Python 2.1.1 license.

Содержание


Предисловие

Эта книга не предназначена для новичков. Подразумевается, что:

Если вы только начали изучать программирование, это не значит, что вы не можете изучать Python. Python — один из самых простых языков для обучения, но для начинающих предназначены совсем другие книги. Я настоятельно рекомендую Learning to Program и How to Think Like a Computer Scientist, а на сайте Python.org вы найдете ссылки на другие введения в язык программирования Python для не программистов.


Глава 1. Знакомство с языком Python

Содержание

1.1. В глубь

Здесь представлена полноценная программа на языке Python.

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

Пример 1.1. odbchelper.py

Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.


def buildConnectionString(params):
    """Создает и возвращает строку соединения из словаря параметров."""
    return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

if __name__ == "__main__":
    myParams = {"server":"mpilgrim", \
                "database":"master", \
                "uid":"sa", \
                "pwd":"secret"}
    print buildConnectionString(myParams)

Теперь запутите программу и посмотрите, что произойдет.

Подсказка
В Python IDE под Windows вы можете запустить программу из меню File->Run... (Ctrl-R). Вывод программы отображается в интерактивном окне.
Подсказка
В Python IDE on Mac OS вы также можете запустить программу из меню Python->Run window... (Cmd-R), но сначала необходимо выставить одну важную опцию. Откройте модуль в IDE, откройте меню модуля, нажав на черный треугольник в верхнем правом углу окна модуля, и убедитесь, что установлена опция “Run as __main__”. Эта опция сохраняется вместе с модулем, так что ее необходимо установить для каждого модуля только один раз.
Подсказка
В UNIX-совместимых система (включая Mac OS X) вы можете запустить программу из командной строки: python odbchelper.py

Пример 1.2. Вывод odbchelper.py

server=mpilgrim;uid=sa;database=master;pwd=secret

1.2. Объявление функций

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

Пример 1.3. Определение функции buildConnectionString


def buildConnectionString(params):

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

Во-вторых, вы не определяете тип возвращаемого значения. В языке Python никогда не указывается не только тип возвращаемого значения, но даже его наличие. На самом деле каждая функция возвращает значение; если функция выполняет инструкцию return, она возвращает указанное в ней значени, иначе функция возвращает специальное значение — None.

Замечание
В языке Visual Basic определение функций (возвращающих значение) начинается с ключевого слова function, а подпрограмм (не возвращающих значение) — с sub. В языке Python нет подпрограмм. То, что называется подпрограммой в других языках, в языке Python является функцией. Любая функция возвращает значение, даже если это None, и определение любой функции начинается с def.

В третьих, не указан тип аргумента params. В языке Python тип переменных никогда не указывается явно. Python отслеживает тип объектов во время выполнения программы.

Замечание
В Java, C++ и других языках со статической типизацией вы должны указывать тип возвращаемого значения и аргументов функции. В языке Python вы никогда не указываете тип. Тип переменной определяется, когда вы присваиваете ей значение.

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

Язык со статической типизацией
Язык, в котором тип (переменной, возвращаемого значения) должен быть указан, например Java или C.
Язык с динамической типизацией
Язык, в котором тип определяется во время выполнения. VBScript и Python являются языками с динамической типизацией, так как они определяют тип переменной, когда ей присваивается значение.
Язык со строгой типизацией
Язык, в котором тип всегда четко определен, например Java или Python. Например, когда целое число не можете быть вами использовано в качестве строки без явного преобразования (мы опишем, как это сделать ниже).
Язык со слабой типизацией
Язык, в котороом типы можно игнорировать. Например в VBScript вы можете объединить строку '12' с целым числом 3 для получения строки '123', которую затем будете использовать в качестве целого числа 123, и все это без явных преобразований.

Таким образом Python является языком со строгой (тип переменной имеет значение) динамической (не используется объявление типа) типизацией.


1.3. Документирование функций

В языке Python вы можете документировать функции, снабжая их строками документации.

Пример 1.4. Определение строки документации для buildConnectionString


def buildConnectionString(params):
    """Создает и возвращает строку соединения из словаря параметров."""

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

Замечание
Утроенные кавычки являются удобным способом определения строк, содержащих одновременно одинарные и двойные кавычки, аналогично qq/.../ в Perl.

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

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

Дополнительная литература


1.4. Все является объектами

Если вы не обратили внимание, я только что заметил, что функции в языке Python имеют атрибуты, и эти атрибуты доступны во время выполнения программы.

Функция, как и все остальное в языке Python, является объектом.

Пример 1.5. Доступ к строке документации функции buildConnectionString

>>> import odbchelper                              1
>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> print odbchelper.buildConnectionString(params) 2
server=mpilgrim;uid=sa;database=master;pwd=secret
>>> print odbchelper.buildConnectionString.__doc__ 3
Создает и возвращает строку соединения из словаря параметров.
1 В первой строке импортируется odbchelper в качестве модуля. После того, как модуль проимпортирован, вы можете ссылаться на его функции, классы или атрибуты. Модули также могут импортировать другие модули для доступа к их функциональности, и вы можете импортировать модули из IDE. Это очень важная концепция, и мы поговорим о ней позже.
2 Для использования функции из импортированного модуля вы должны указать имя этого модуля. То есть, вместо buildConnectionString необходимо написать odbchelper.buildConnectionString. Если вы использовали классы в Java, то заметили отдаленное сходство с ними.
3 Вместо привычного вызова функции мы запрашиваем один из ее атрибутов — __doc__.
Замечание
import в Python работает аналогично require в Perl. Проимпортировав в Python один раз модуль с помощью инструкции import вы можете обращаться к его функциям в виде module.function; проимпортировав модуль с помощью инструкции require в Perl, вы можете обращаться к его функциям в виде module::function.

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

Это настолько важно, что я повторю еще раз: в языке Python все является объектами. Строки являются объектами. Списки являются объектами. Функции являются объектами. И даже модули, как мы скоро увидим, являются объектами.

Дополнительная литература

Footnotes

[1] Различные языки определяют понятие “объект” по-разному. В некоторых языках все объекты должны иметь атрибуты и методы, в других — от любых объектов можно породить подклассы. Python определяет понятие объекта гораздо шире: некоторые объекты не имеют ни атрибутов, ни методов, и для всех объектов могут быть порождены подклассы (более подробно читайте об этом в главе 3). Однако любой объект может быть присвое переменной или использован в качестве аргумента функции. (более подробно читайте об этом в главе 2).


1.5. Отступы

В определении функций нет явных begin и end, нет фигурных скобок, обозначающих начало и конец тела функции. Единственным разделителем является двоеточие (“:”) плюс отсуп кода в теле функции.

Пример 1.6. Отступы в определении функции buildConnectionString


def buildConnectionString(params):
    """Создает и возвращает строку соединения из словаря параметров."""
    return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

Блоки кода (функции, инструкций if, for и др.) определяются отступами. Увеличение отступа обозначает начало блока и уменьшение — его конец. Никакие дополнительные скобки или ключевые слова для этих целей не используются. Таким образом отступы являются значимыми и должны быть согласованными. В приведенном примере тело функции (включая строку документации) набрано с отступом в четыре пробела. Число пробелов в отступе не обязательно должно быть равно четырем, но должно быть везде одинаковым. Первая строка, без отступа, находится за пределами тела функции.

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

Замечание
Python использует переход на новую строку для разделения инструкций и отступы для выделения блоков. В C++ и Java для разделения инструкций используется точка с запятой, а блоки выделяются фигурными скобками.

Дополнительная литература


1.6. Тестирование модулей

Модули в языке Python также являются объектами и имеют несколько полезных атрибутов. Вы можете использовать их, например, для тестирования.

Пример 1.7. Прием с if __name__


if __name__ == "__main__":

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

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

Так что же делает этот прием с if? Модули являются объектами и имеют атрибут __name__. Значение этого атрибута зависит от того, как используется модуль. Если вы импортируете модуль, атрибут __name__ равен имени файла без каталога и расширения. Но если вы запускаете модуль как отдельную программу, __name__ будет иметь специальное значение — __main__.

Пример 1.8. __name__ импортированного модуля

>>> import odbchelper
>>> odbchelper.__name__
'odbchelper'

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

Подсказка
Для того, чтобы данный прием работал в MacPython, необходимо выполнить дополнительные шаги. Откройте меню опций модуля, нажав на черный треугольник в правом верхнем углу, и убедитесь, что отмечен пункт Run as __main__.

Дополнительная литература


1.7. Словари

Сделаем небольшое отступление, так как вам необходимо познакомиться со словарями, кортежами и списками. Если вы знаток Perl, то уже имеете некоторое представление о словарях и списках, но вам, тем не менее, необходимо обратить внимание на кортежи.

Один из втроенных типов языка Python, словарь, сопоставляет ключам значения.

Замечание
Словари в Python аналогичны хешам в Perl. В Perl имена переменных, хранящих хеши, всегда начинаются с символа %. Переменные в языке Python могут иметь произвольное имя, интерпретатор сам определяет их тип.
Замечание
Словари в Python схожи с экземплярами класса Hashtable в Java.
Замечание
Словари Python схожи с экземплярами объекта Scripting.Dictionary в Visual Basic.

Пример 1.9. Определени словарей

>>> d = {"server":"mpilgrim", "database":"master"} 1
>>> d
{'server': 'mpilgrim', 'database': 'master'}
>>> d["server"]                                    2
'mpilgrim'
>>> d["database"]                                  3
'master'
>>> d["mpilgrim"]                                  4
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
KeyError: mpilgrim
1 Сначала мы создаем словарь с двумя записями и присваиваем его переменной d. Каждая запись является парой ключ-значение, весь набор записей перечисляется в фигурных скобках.
2 "server" является ключом, а ассоциированное с ним значение, d["server"], равно "mpilgrim".
3 "database" является ключом, а ассоциированное с ним значение, d["database"], равно "master".
4 Вы можете определить значение по ключу, но не можете определить ключи по значению. Так, выражение d["server"] дает "mpilgrim", но d["mpilgrim"] генерирует исключение, так как словарь не содержит записи с ключом "mpilgrim".

Пример 1.10. Изменение словарей

>>> d
{'server': 'mpilgrim', 'database': 'master'}
>>> d["database"] = "pubs" 1
>>> d
{'server': 'mpilgrim', 'database': 'pubs'}
>>> d["uid"] = "sa"        2
>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
1 Ключи в словаре уникальны. Присваивание нового значения с существующим ключом заменяет старое.
2 В любой момент вы можете добавить новую запись. Синтаксис добавления записи аналогичен синтаксису замены значения в записи с существующим ключом. Возможно, в какой-то момент вы будете думать, что добавляете новую запись, в то время как на самом деле вы только изменяете значение в уже существующей записи.

Обратите внимание, что новая запись (ключ "uid", значение "sa") появилась в середине. В самом деле, то что записи появились в том же порядке, в котором были введены — это случайность.

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

Пример 1.11. Смешивание различных типов данных в словарях

>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
>>> d["retrycount"] = 3 1
>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3}
>>> d[42] = "douglas"   2
>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
1 Словари предназначены не только для строк. Значения в словарях могут быть любого типа: строки, числа и даже другие словари. Совсем не обязательно, чтобы все значения в одном словаре были одного типа, по мере необходимости вы можете смешивать объекты различных типов.
2 На ключи в словарях все же накладываются некоторые ограничения. Они могут быть строками, числами и объектами некоторых других типов (более подробно об этом мы раскажем позже). Вы также можете использовать ключи разного типа в одном словаре.

Пример 1.12. Удаление записей из словарей

>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
>>> del d[42] 1
>>> d
{'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3}
>>> d.clear() 2
>>> d
{}
1 Инструкция del позволяет удалять отдельные записи из словаря по ключу.
2 Метод clear удаляет все записи из словаря. Обратите внимание, что пустые фигурные скобки определяют словарь, не содержащий ни одной записи.

Пример 1.13. Регистр букв в строках имеет значение

>>> d = {}
>>> d["key"] = "value"
>>> d["key"] = "other value" 1
>>> d
{'key': 'other value'}
>>> d["Key"] = "third value" 2
>>> d
{'Key': 'third value', 'key': 'other value'}
1 Присваивание значения с существующим ключом просто заменяет старое значение на новое.
2 В этом случае мы не присваиваем значение с существующим ключом, так как Python различает регистр букв в строках, и строка 'key' не равна 'Key'. Буквы разного регистра могут выглядеть для вас похожими, но для интерпретатора они совершенно разные. Таким образом в данном случае в словарь добавляется новая запись.

Дополнительная литература


1.8. Списки

Список являются одним из самых используемых типов данных в языке Python. Если все ваше знакомство со списками ограничивается массивами в Visual Basic или (не дай бог) datastore в Powerbuilder, возмите себя в руки для знакомства со списками в языке Python.

Замечание
Списки в языке Python похожи на массивы в языке Perl. Имена пееменных, хранящих массивы, в языке Perl всегда начинаются с символа @. Python не накладывает никаких дополнительных ограничений на имя переменных, в которых хранятся списки, интерпретатор сам определяет тип переменной.
Замечание
Списки в языке Python — нечто большее, чем массивы в Java (хотя вы можете использовать их в таком качестве, если это все, что вам требуется от жизни). Более близкой аналогией будет класс Vector, способный содержать произвольные объекты и динамически увеличиваться при добавлении новых элементов.

Пример 1.14. Определение списков

>>> li = ["a", "b", "mpilgrim", "z", "example"] 1
>>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[0]                                       2
'a'
>>> li[4]                                       3
'example'
1 Сначала мы определяем список из пяти элементов. Обратите внимание, что исходный порядок элементов сохраняется. Это не случайно, список является упорядоченным множеством элементов, перечисленных в квадратных скобках.
2 Списки могут быть использованы в качестве массивов. Отсчет элементов всегда ведется от нуля, так что первый элемент непустого списка — li[0].
3 Последний элемент списка из пяти элементо — li[4], так как отсчет ведется от нуля.

Пример 1.15. Отрицательные индексы

>>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[-1] 1
'example'
>>> li[-3] 2
'mpilgrim'
1 При использовании отрицательных индексов отсчет ведется с конца списка. Послединий элемент любого непустого списка можно получить, используя выражение li[-1].
2 Если вас смущают отрицательные индексы, запомните простое правило: li[-n] == li[len(li) - n]. То есть, в нашем случае li[-3] == li[5 - 3] == li[2].

Пример 1.16. Срез

>>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[1:3]  1
['b', 'mpilgrim']
>>> li[1:-1] 2
['b', 'mpilgrim', 'z']
>>> li[0:3]  3
['a', 'b', 'mpilgrim']
1 Указав через двоеточие два индекса, вы можете получить подмножество элементов списка, называемое “срезом”. Получаемое значение является новым списком, содержащим все элементы исходного списка в том же порядке, начиная с первого индекса (здесь li[1]) до, но не включая, второго индекса (здесь li[3]).
2 В операции среза также можно использовать отрицательные идексы. Если это поможет, считайте первый индекс соответствующим первому элементу, который вам нужен, и второй — первому элементу, который не нужен. Получаемое значение — все, что между ними находится.
3 Нумерация элементов начинается с нуля, так что li[0:3] дает первые три элемента исходного списка.

Пример 1.17. Сокращения в записи среза

>>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[:3] 1
['a', 'b', 'mpilgrim']
>>> li[3:] 2
['z', 'example']
>>> li[:]  3
['a', 'b', 'mpilgrim', 'z', 'example']
1 Если первый индекс среза равен нулю, то его можно опустить. Аналогично можно опустить второй индекс, если он равен длине списка. То есть li[:3] дает такой же результат, как и li[0:3] в предыдущем примере.
2 Обратите внимание на симметрию. Для данного списка из пяти элементов li[:3] дает первые три элемента и li[3:] — последние два. В самом деле, li[:n] всегда дает первые n элементов, а li[n:] — все остальное.
3 Если опущены оба индекса, будут включены все элемента исходного списка. Но это не тот же список, это новый список с теми же элементами. Таким образом, li[:] позволяет создать копию списка.

Пример 1.18. Добавление элементов в список

>>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li.append("new")               1
>>> li
['a', 'b', 'mpilgrim', 'z', 'example', 'new']
>>> li.insert(2, "new")            2
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new']
>>> li.extend(["two", "elements"]) 3
>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
1 Метод append добавляет один элемент в конец списка.
2 Метод insert вставляет один элемент в список. Целочисленный аргумент является индексом первого элемента, позиция которого изменится. Обратите внимание, что элементы списка могут быть не уникальными — после этой операции в списке содержится два элемента со значением "new", li[2] и li[6].
3 Метод extend добавляет в конец элементы другого списка. В данном случае второй список содержит два элемента.

Пример 1.19. Поиск в списке

>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>> li.index("example") 1
5
>>> li.index("new")     2
2
>>> li.index("c")       3
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: list.index(x): x not in list
>>> "c" in li           4
0
1 Метод index находит первое вхождение элемента в списке и возвращает его индекс.
2 index позволяет найти только первое вхождение элемента. В нашем списке строка "new" присутствует дважды (li[2] и li[6]), но метод index вернет индекс только первого — 2.
3 Если указанный элемент в списке не найден, генерируется исключение. Такое поведение заметно отличается от поведения аналогичных средств в других языках, возвращающих какой-либо некорректный индекс. Генерация исключения более удобна, так как работа программы останавливается в месте возникновения ошибки, а не в момент использования некорректного индекса.
4 Для проверки наличия элемента в списке используйте оператор in, возвращающий 1, если значение найдено, и 0, если в списке такого значения нет.
Замечание
Python не имеет отдельного булева типа. В булевом контексте (например, в условии инструкции if), 0 является ложью, а все остальные сисла являются истиной. Аналогично и для других типов: пустая строка (""), список ([]) и словарь ({}) являются ложью, а все остальные строки, списки и словари — истиной.

Пример 1.20. Удаление элементов из списка

>>> li
['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>> li.remove("z")   1
>>> li
['a', 'b', 'new', 'mpilgrim', 'example', 'new', 'two', 'elements']
>>> li.remove("new") 2
>>> li
['a', 'b', 'mpilgrim', 'example', 'new', 'two', 'elements']
>>> li.remove("c")   3
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: list.remove(x): x not in list
>>> li.pop()         4
'elements'
>>> li
['a', 'b', 'mpilgrim', 'example', 'new', 'two']
1 Метод remove удаляет из списка первый элемент с указанным значением.
2 remove удаляет только один элемент. В данном случае строка "new" присутствует в списке дважды, но li.remove("new") удалит только первую.
3 Если элемент с указанным значением в списке не найден, remove, как и index, генерирует исключение.
4 Метод pop выполняет сразу два действия: удаляет последний элемент из списка и возвращает его. Этим он отличается от li[-1], возвращающего последний элемент, но не изменяющего список, и li.remove(value), изменяющего список, но не возвращающего элемент.

Пример 1.21. Применение операторов к спискам

>>> li = ['a', 'b', 'mpilgrim']
>>> li = li + ['example', 'new'] 1
>>> li
['a', 'b', 'mpilgrim', 'example', 'new']
>>> li += ['two']                2
>>> li
['a', 'b', 'mpilgrim', 'example', 'new', 'two']
>>> li = [1, 2] * 3              3
>>> li
[1, 2, 1, 2, 1, 2]
1 С помощью оператора + можно “склеивать” списки. list = list + otherlist эквивалентно list.extend(otherlist), но при этом создается новый список, в то время как extend изменяет существующий.
2 Python поддерживает операцию +=. li += ['two'] полностью эквивалентно li.extend(['two']). Операция += работает для списков, строк, чисел и может быть переопределена для классов (более подробно о классах читайте в главе 3).
3 Оператор * размножает элементы списка. li = [1, 2] * 3 эквивалентно li = [1, 2] + [1, 2] + [1, 2].

Дополнительная литература


1.9. Кортежи

Кортеж — это неизменяемый список. С момента создания кортеж не может быть изменен никакими способами.

Пример 1.22. Определение кортежей

>>> t = ("a", "b", "mpilgrim", "z", "example") 1
>>> t
('a', 'b', 'mpilgrim', 'z', 'example')
>>> t[0]                                       2
'a'
>>> t[-1]                                      3
'example'
>>> t[1:3]                                     4
('b', 'mpilgrim')
1 Кортеж определяется так же, как и список, но элементы перечисляются в круглых скобках вместо квадратных.
2 Как и в списках, элементы в кортежах имеют определенный порядок. Точно так же нумерация элементов начинается с нуля, то есть первым элементом непустого кортежа всегда является t[0].
3 Как и для списков, отрицательные индексы позволяют вести отсчет элементов с конца кортежа.
4 К кортежам, как и к спискам можно применить операцию среза. Обратите внимание, что срез списка — новый список, а срез кортежа — новый кортеж.

Пример 1.23. У кортежей нет методов

>>> t
('a', 'b', 'mpilgrim', 'z', 'example')
>>> t.append("new")    1
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'append'
>>> t.remove("z")      2
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'remove'
>>> t.index("example") 3
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'index'
>>> "z" in t           4
1
1 Вы не можете добавлять элементы в кортеж. У кортежей нет методов append и extend.
2 Вы не можете удалять элементы из кортежа. У кортежей нет методов remove и pop.
3 Вы не можете искать элементы в кортеже с помощью метода index — у кортежей нет такого метода.
4 Однако, вы можете проверить наличие элемента в кортеже с помощью оператора in.

Так для чего же нужны кортежи?

Замечание
Кортеж может быть преобразован в список и наоборот. Встроенная функция tuple воспринимает список в качестве аргумента и возвращает кортеж с теми же самыми элементами, и функция list воспринимает кортеж в качестве аргумента и возвращает список. В результате tuple “замораживает” список, а list его “размораживает”.

Дополнительная литература

Footnotes

[2] На самом деле все несколько сложнее. Ключи словаря должны быть неизменяемыми. Кортежи сами по себе неизменяемы, но, если у вас имеется кортеж списков, то он считается изменяемым и не может быть использован в качесве ключа словаря. Только кортежи чисел, строк и других таких же кортежей могут быть ключами словаря.


1.10. Определение переменных

После того, как вы познакомились со словарями, кортежами и списками, давайте вернемся к нашему примеру программы — odbchelper.py.

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

Пример 1.24. Определение переменной myParams


if __name__ == "__main__":
    myParams = {"server":"mpilgrim", \
                "database":"master", \
                "uid":"sa", \
                "pwd":"secret"}

Здесь есть несколько интересных моментов. Во-первых, обратите внимание на отступы. Инструкция if является составной и ее тело должно вводиться с отступом, как и тело функции.

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

Замечание
Если инструкция разбита на несколько строк с использованием признака наличия продолжения (“\”), строки продолжения могут иметь произвольный отступ. В этом случае строгие правила использования отступов не действуют. Если ваш Python IDE автоматически выставляет отступы для строк продолжения, следует, по-видимому, принять их в качестве умолчания, если у вас нет весомой причины от этого отказаться.
Замечание
Строго говоря, выражения в круглых, квадратных или фигурных скобках (например, определения словарей) могут быть разбиты на несколько строк и без использования явного признака наличия продолжения (“\”). Я предпочитаю использовать обратный слэш даже если в нем нет необходимости, так как, на мой взгляд, это облегчает чтение кода. Но это уже дело вкуса.

В третьих, вы нигде не объявляли переменную myParams, а просто присвоили ей значение. Это соответствует поведению VBScript без опции option explicit. К счастью, в отличии от VBScript, Python не позволит ссылаться на переменную, которой нигде не было присвоено значение — при попытке это сделать будет сгенерировано исключение.

Пример 1.25. Использование несуществующей переменной

>>> x
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
NameError: There is no variable named 'x'
>>> x = 1
>>> x
1

В один прекраснй день вы будете благодарны языку Python за такое поведение.

Дополнительная литература


1.11. Присваивание сразу нескольких значений

Одна из приятных возможностей языка Python — использование последовательностей для односременного присваивания нескольких значений.

Пример 1.26. Присваивание сразу нескольких значений

>>> v = ('a', 'b', 'e')
>>> (x, y, z) = v 1
>>> x
'a'
>>> y
'b'
>>> z
'e'
1 v — кортеж из трех элементов и (x, y, z) — кортеж из трех элементов. Присваивание одного другому приводит к присваиванию каждого значения из v соответствующей переменной.

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

Пример 1.27. Присваивание идущих подряд значений

>>> range(7)                                                                    1
[0, 1, 2, 3, 4, 5, 6]
>>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7) 2
>>> MONDAY                                                                      3
0
>>> TUESDAY
1
>>> SUNDAY
6
1 Встроенная функция range возвращает список целых чисел. В простейшем случае она воспринимает в качестве аргумента верхний предел и возвращает список целых чисел от нуля до обозначенного придела, но не включая предельное значение. При желании вы можете указать нижний предел отличный от нуля и шаг отличный от еденицы. Выполните инструкцию print range.__doc__ для получения более подробной информации.
2 MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY и SUNDAY — переменные, которые мы определяем. Этот пример взят из модуля calendar, печатающего календари, аналогично программе cal в UNIX. Этот модуль определяет целочисленные константы для дней недели.
3 Теперь каждая переменная имеет свое значение: MONDAY равна 0, TUESDAY — единице, и т. д.

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

Дополнительная литература


1.13. Обработка списков

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

Пример 1.30. Введение в расширенную запись списков

>>> li = [1, 9, 8, 4]
>>> [elem*2 for elem in li]      1
[2, 18, 16, 8]
>>> li                           2
[1, 9, 8, 4]
>>> li = [elem*2 for elem in li] 3
>>> li
[2, 18, 16, 8]
1 Чтобы понять такую запись, читайте ее справа налево. li — список, который вы преобразуете. Python пробегает по всем элементам li, временно присваивает каждый из них переменной elem, вычисляет значение выражения elem*2 и добавляет в конец списка, который вы в результате получаете.
2 Обратите внимание, что при использовании расширенной записи списка исходный список не изменяется.
3 Присваивание результата переменной, в которой хранится обрабатываемый список, безопасно, так как новый список создается в памяти и, когда вычисление выражения закончено, он присваивается переменной.

Пример 1.31. Расширенная запись списка в buildConnectionString

["%s=%s" % (k, v) for k, v in params.items()]

Во-первых, обратите внимание на вызов метода items словаря params. Этот метод возвращает список кортежей, которые представляют записи в словаре.

Пример 1.32. keys, values, and items

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.keys()   1
['server', 'uid', 'database', 'pwd']
>>> params.values() 2
['mpilgrim', 'sa', 'master', 'secret']
>>> params.items()  3
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
1 Метод keys словаря возвращает список всех ключей. Порядок следования ключей в этом списке может не соответствовать порядку, в котором определялись записи словаря (помните, записи в словаре не упорядочены).
2 Метод values возвращает список всех значений. Значения в этом списке идут в таком же порядке, как и ключи в списке, возвращаемом методом keys, то есть params.values()[n] == params[params.keys()[n]] для всех n.
3 Метод items возвращает список всех записей словаря в виде кортежа (key, value).

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

Пример 1.33. Расширенная запись списка в buildConnectionString, шаг за шагом

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
>>> [k for k, v in params.items()]                1
['server', 'uid', 'database', 'pwd']
>>> [v for k, v in params.items()]                2
['mpilgrim', 'sa', 'master', 'secret']
>>> ["%s=%s" % (k, v) for k, v in params.items()] 3
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
1 Обратите внимание, что мы используем две переменные при переборе элементов списка params.items(). Это — еще один пример использования множественного присваивания. Первым элементом списка params.items() является кортеж ('server', 'mpilgrim'), поэтому для первой итерации k получит значение 'server' и v получит значение 'mpilgrim'. В данном случае мы игнорируем v и включаем в результат только k, так что в результате мы получим список, эквивалентный params.keys().
2 Здесь мы делаем то же самое, но игнорируем k, так что в результате мы получим список, эквивалентный params.values().
3 Комбинируя эти два примера с простым форматированием, мы получаем список строк, содержащих ключ и значение для каждой записи словаря. Результат получается подозрительно похожим на вывод программы, все что осталось сделать — это объединить все элементы списка в одну строку.

Дополнительная литература


1.12. Форматированное представление

Python позволяет получить форматированное представление значений в виде строки. Хотя строка формата может содержать довольно сложные выражения, чаще всего используется вставка значений в строку с помощью шаблона %s.

Замечание
Python использует для строк формата такой же синтаксис, как и функция sprintf в C.

Пример 1.28. Введение в форматирование

>>> k = "uid"
>>> v = "sa"
>>> "%s=%s" % (k, v) 1
'uid=sa'
1 При вычислении всего выражения получается строка. Первый шаблон %s заменяется значением k, второй — значением v. Все остальные символы в строке формата (в данном случае — знак равенства) попадают в результат без изменений.

Обратите внимание, что (k, v) является кортежем. Я уже говорил, что кортежи весьма полезный тип данных.

Вы можете подумать, что здесь слишком много работы для простого объединения строк. И вы будете правы, но форматирование — это не просто объединение строк. Это даже не просто форматирование. Данная операция также выполняет приведение типа.

Пример 1.29. Форматирование vs. объединение

>>> uid = "sa"
>>> pwd = "secret"
>>> print pwd + " is not a good password for " + uid      1
secret is not a good password for sa
>>> print "%s is not a good password for %s" % (pwd, uid) 2
secret is not a good password for sa
>>> userCount = 6
>>> print "Users connected: %d" % (userCount, )           3 4
Users connected: 6
>>> print "Users connected: " + userCount                 5
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
TypeError: cannot add type "int" to string
1 Оператор + позволяет объединять строки.
2 В простейшем случае форматирование дает такой же результат, как и объединение строк.
3 (userCount, ) — кортеж с одним элементом. Да, его синтаксис выглядит немного странно, но для этого есть весомая причина: только так можно однозначно определить кортеж. На самом деле, вы всегда можете ставить запятую после последнего элемента в определении списка, кортежа или словаря, но запятая необходима, если вы определяете кортеж из одного элемента. Если бы запятая не была обязательной, интерпретатор не смог бы определить, что имелось ввиду под выражением (userCount) — кортеж с одним элементом или просто значение userCount.
4 Форматирование также работает и с целыми числами при использовании шаблона %d вместо %s.
5 При попытке сложить строку со значением не строкового типа генерируется исключение. В отличии от форматирования, простое сложение работает, только если все значения уже являются строками.

Дополнительная литература


1.14. Объединение и разбиение строк

Вы имеете список строк с парами ключ-значение вида key=value и хотите объединить их в одну строку. Для этих целей можно воспользоваться методом join строковых объектов.

Пример 1.34. Объединение строк в buildConnectionString

    return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

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

В любом случае, метод join объединяет элементы списка в одну строку, в которой элементы оказываются разделены точкой с запятой. Совсем не обязательно, чтобы в качестве разделителя выступала точка с запятой, не обязательно, чтобы он был одним символом — разделитель может быть произвольной строкой.

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

Пример 1.35. Вывод программы odbchelper.py

>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> ["%s=%s" % (k, v) for k, v in params.items()]
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> ";".join(["%s=%s" % (k, v) for k, v in params.items()])
server=mpilgrim;uid=sa;database=master;pwd=secret

Именно эту строку возвращает функция buildConnectionString и выводит программа.

Историческая справка. Когда я начал изучать Python, я ожидал, что join будет методом списка, а строка-разделитель — его аргументом. Многие люди думали точно также. До появления версии Python 1.6 строки не имели всех этих полезных методов. Был отдельный модуль string, содержащий функции для работы со строками. Каждая функция воспринимала строку в качесве первого аргумента. Функции посчитали достаточно важными, чтобы поместить их непосредственно в строковые объекты, что имеет смысл для таких функций, как lower, upper и split. Однако многие программисты обратили внимание на новый метод join, приводя аргументы в пользу того, что он должен быть методом списка или остаться частью старого модуля string (в котором и сейчас содержится много полезных вещей). Сам я использую новый метод join, но вы можете увидеть код написанный и по-другому. Если это вас беспокоит, вы можете использовать вместо него старую функцию string.join.

Вы, наверное, зададите вопрос, есть ли аналогичный метод для разбиения строки, возвращающий список подстрок? Да, конечно, и называется он split.

Пример 1.36. Разбиение строк

>>> li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> s = ";".join(li)
>>> s
'server=mpilgrim;uid=sa;database=master;pwd=secret'
>>> s.split(";")    1
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> s.split(";", 1) 2
['server=mpilgrim', 'uid=sa;database=master;pwd=secret']
1 Метод split выполняет действия, обратные методу join, разбивая строку на подстроки. Обратите внимание, что сам разделитель (“;”) не содержится в результате.
2 Метод split воспринимает второй необязательный аргумент, указывающий максимальное число разбиений. (“О-о-о, необязательные аргументы…” Вы узнаете, как определить функцию с необязательными аргументами, в следующей главе.)
Замечание
Вызов anystring.split(delimiter, 1) может быть полезен, если вы хотите найти первое вхождение подстроки и, затем, работать с фрагментами до этой подстроки (он является первым элементом возвращаемого списка) и после него (второй элемент возвращаемого списка).

Дополнительная литература


Начало > В глубь языка Python > Знакомство с языком Python > Заключение   <<   >>diveinto.python.ru
Для программистов

1.15. Заключение

Теперь программа odbchelper.py и ее вывод приобретают смысл.

Пример 1.37. odbchelper.py


def buildConnectionString(params):
    """Создает и возвращает строку соединения из словаря параметров."""
    return ";".join(["%s=%s" % (k, v) for k, v in params.items()])

if __name__ == "__main__":
    myParams = {"server":"mpilgrim", \
                "database":"master", \
                "uid":"sa", \
                "pwd":"secret"}
    print buildConnectionString(myParams)

Пример 1.38. Вывод программы odbchelper.py

server=mpilgrim;uid=sa;database=master;pwd=secret

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


Приложение A. GNU Free Documentation License

Содержание

Version 1.1, March 2000

Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.

A.0. Preamble

The purpose of this License is to make a manual, textbook, or other written document "free" in the sense of freedom: to assure everyone the effective freedom to copy and redistribute it, with or without modifying it, either commercially or noncommercially. Secondarily, this License preserves for the author and publisher a way to get credit for their work, while not being considered responsible for modifications made by others.

This License is a kind of "copyleft", which means that derivative works of the document must themselves be free in the same sense. It complements the GNU General Public License, which is a copyleft license designed for free software.

We have designed this License in order to use it for manuals for free software, because free software needs free documentation: a free program should come with manuals providing the same freedoms that the software does. But this License is not limited to software manuals; it can be used for any textual work, regardless of subject matter or whether it is published as a printed book. We recommend this License principally for works whose purpose is instruction or reference.


3.16. Summary

The fileinfo.py program should now make perfect sense.

Пример 3.40. fileinfo.py

"""Получение метаинформации, специфичной для файла данного типа.

Создайте экземпляр соответствующего класса, передав конструктору имя файла.
Возвращаемый объект ведет себя аналогично словарю с парами ключ-значение для
каждой части метаинформации.
    import fileinfo
    info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3")
    print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()])

Или используйте функцию listDirectory для получения информации обо всех файлов
в директории
    for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
        ...

Модуль может быть расширен путем доюавления классов для других типов файлов,
например HTMLFileInfo, MPGFileInfo, DOCFileInfo.  Каждый класс полностью
отвечает за анализ файлов соответствующего типа; используйте MP3FileInfo в
качестве примера.
"""
import os
import sys
from UserDict import UserDict

def stripnulls(data):
    "очищает строку от символов пропуска и нулевых символов"
    return data.replace("\00", "").strip()

class FileInfo(UserDict):
    "хранит метаинформацию о файле"   
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

class MP3FileInfo(FileInfo):
    "хранит ID3v1.0 MP3 теги"
    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}

    def __parse(self, filename):
        "анализ ID3v1.0 тегов из MP3 файла"
        self.clear()
        try:                               
            fsock = open(filename, "rb", 0)
            try:                           
                fsock.seek(-128, 2)        
                tagdata = fsock.read(128)  
            finally:                       
                fsock.close()              
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items():
                    self[tag] = parseFunc(tagdata[start:end])               
        except IOError:                    
            pass                           

    def __setitem__(self, key, item):
        if key == "name" and item:
            self.__parse(item)
        FileInfo.__setitem__(self, key, item)

def listDirectory(directory, fileExtList):                                        
    """возвращает список объектов с метаинформацией для всех файлов с
указанным расширением"""                       
    fileList = [os.path.normcase(f) for f in os.listdir(directory)]               
    fileList = [os.path.join(directory, f) for f in fileList \
                if os.path.splitext(f)[1] in fileExtList]                         
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):      
        "оределяет класс, предназначеный для обработки файла, по расширению"      
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]       
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
    return [getFileInfoClass(f)(f) for f in fileList]                             

if __name__ == "__main__":
    for info in listDirectory("/music/_singles/", [".mp3"]):
        print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
        print

Before diving into the next chapter, make sure you're comfortable doing all of these things:


Глава 3. Средства объектно-ориентированного программирования

Содержание

3.1. В глубь

Начиная с этой главы мы будем иметь дело с объектно ориентированным программированием на языке Python. Помните, я говорил, что вам необходимо знать объектно-ориентированный язык для чтения этой книги? Так я не шутил.

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

Пример 3.1. fileinfo.py

Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.

"""Получение метаинформации, специфичной для файла данного типа.

Создайте экземпляр соответствующего класса, передав конструктору имя файла.
Возвращаемый объект ведет себя аналогично словарю с парами ключ-значение для
каждой части метаинформации.
    import fileinfo
    info = fileinfo.MP3FileInfo("/music/ap/mahadeva.mp3")
    print "\\n".join(["%s=%s" % (k, v) for k, v in info.items()])

Или используйте функцию listDirectory для получения информации обо всех файлов
в директории
    for info in fileinfo.listDirectory("/music/ap/", [".mp3"]):
        ...

Модуль может быть расширен путем доюавления классов для других типов файлов,
например HTMLFileInfo, MPGFileInfo, DOCFileInfo.  Каждый класс полностью
отвечает за анализ файлов соответствующего типа; используйте MP3FileInfo в
качестве примера.
"""
import os
import sys
from UserDict import UserDict

def stripnulls(data):
    "очищает строку от символов пропуска и нулевых символов"
    return data.replace("\00", "").strip()

class FileInfo(UserDict):
    "хранит метаинформацию о файле"   
    def __init__(self, filename=None):
        UserDict.__init__(self)
        self["name"] = filename

class MP3FileInfo(FileInfo):
    "хранит ID3v1.0 MP3 теги"
    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}

    def __parse(self, filename):
        "анализ ID3v1.0 тегов из MP3 файла"
        self.clear()
        try:                               
            fsock = open(filename, "rb", 0)
            try:                           
                fsock.seek(-128, 2)        
                tagdata = fsock.read(128)  
            finally:                       
                fsock.close()              
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items():
                    self[tag] = parseFunc(tagdata[start:end])               
        except IOError:                    
            pass                           

    def __setitem__(self, key, item):
        if key == "name" and item:
            self.__parse(item)
        FileInfo.__setitem__(self, key, item)

def listDirectory(directory, fileExtList):                                        
    """возвращает список объектов с метаинформацией для всех файлов с
указанным расширением"""                       
    fileList = [os.path.normcase(f) for f in os.listdir(directory)]               
    fileList = [os.path.join(directory, f) for f in fileList \
                if os.path.splitext(f)[1] in fileExtList]                         
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):      
        "оределяет класс, предназначеный для обработки файла, по расширению"      
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]       
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
    return [getFileInfoClass(f)(f) for f in fileList]                             

if __name__ == "__main__":
    for info in listDirectory("/music/_singles/", [".mp3"]): 1
        print "\n".join(["%s=%s" % (k, v) for k, v in info.items()])
        print
1 Вывод этой программы зависит от файлов на вашем диске. Чтобы получить осмысленную информацию, необходимо изменить путь на каталог, содержащий MP3 файлы на вашей машине.

Пример 3.2. Вывод программы fileinfo.py

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

album=
artist=Ghost in the Machine
title=A Time Long Forgotten (Concept
genre=31
name=/music/_singles/a_time_long_forgotten_con.mp3
year=1999
comment=http://mp3.com/ghostmachine

album=Rave Mix
artist=***DJ MARY-JANE***
title=HELLRAISER****Trance from Hell
genre=31
name=/music/_singles/hellraiser.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Rave Mix
artist=***DJ MARY-JANE***
title=KAIRO****THE BEST GOA
genre=31
name=/music/_singles/kairo.mp3
year=2000
comment=http://mp3.com/DJMARYJANE

album=Journeys
artist=Masters of Balance
title=Long Way Home
genre=31
name=/music/_singles/long_way_home1.mp3
year=2000
comment=http://mp3.com/MastersofBalan

album=
artist=The Cynic Project
title=Sidewinder
genre=18
name=/music/_singles/sidewinder.mp3
year=2000
comment=http://mp3.com/cynicproject

album=Digitosis@128k
artist=VXpanded
title=Spinning
genre=255
name=/music/_singles/spinning.mp3
year=2000
comment=http://mp3.com/artists/95/vxp

2.9. Заключение

Теперь код программы apihelper.py обрел смысл.

Пример 2.31. apihelper.py


def help(object, spacing=10, collapse=1):
    """Выводит методы и строки документации.
    
    В качестве аргумента может использоваться модуль, класс, список, словарь
    или строка."""
    methodList = [method for method in dir(object) if callable(getattr(object, method))]
    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

if __name__ == "__main__":
    print help.__doc__

Пример 2.32. Вывод apihelper.py

>>> from apihelper import help
>>> li = []
>>> help(li)
append     L.append(object) -- append object to end
count      L.count(value) -> integer -- return number of occurrences of value
extend     L.extend(list) -- extend list by appending list elements
index      L.index(value) -> integer -- return index of first occurrence of value
insert     L.insert(index, object) -- insert object before index
pop        L.pop([index]) -> item -- remove and return item at index (default last)
remove     L.remove(value) -- remove first occurrence of value
reverse    L.reverse() -- reverse *IN PLACE*
sort       L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1

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


Глава 2. Сила самоанализа

Содержание

2.1. В глубь

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

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

Пример 2.1. apihelper.py

Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.


def help(object, spacing=10, collapse=1): 1 2 3
    """Выводит методы и строки документации.
    
    В качестве аргумента может использоваться модуль, класс, список, словарь
    или строка."""
    methodList = [method for method in dir(object) if callable(getattr(object, method))]
    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)
    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

if __name__ == "__main__":                4 5
    print help.__doc__
1 Этот модуль определяет одну функцию — help. Она воспринимает три аргумента: object, spacing и collapse. Последние два аргумента, как мы скоро увидим, не являются обязательными.
2 Функция help имеет многострочную строку документации, кратко описывающую предназначение функции. Обратите внимание, что здесь не упомянуто возвращаемое значение — функция предназначениа для выполнения определенных действий а не возврата значения.
3 Тело функции выделено отступами.
4 Прием с if __name__ позволяет использовать модуль в качестве программы без каких-либо конфликтов. В данном случае программа просто выводит строку документации функции help.
5 В инструкции if для сравнения используется оператор ==. Заключать в скобки выражение совсем не обязательно.

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

Пример 2.2. Использование apihelper.py

>>> from apihelper import help
>>> li = []
>>> help(li)
append     L.append(object) -- append object to end
count      L.count(value) -> integer -- return number of occurrences of value
extend     L.extend(list) -- extend list by appending list elements
index      L.index(value) -> integer -- return index of first occurrence of value
insert     L.insert(index, object) -- insert object before index
pop        L.pop([index]) -> item -- remove and return item at index (default last)
remove     L.remove(value) -- remove first occurrence of value
reverse    L.reverse() -- reverse *IN PLACE*
sort       L.sort([cmpfunc]) -- sort *IN PLACE*; if given, cmpfunc(x, y) -> -1, 0, 1

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

Пример 2.3. Дополнительные возможности apihelper.py

>>> import odbchelper
>>> help(odbchelper)
buildConnectionString Build a connection string from a dictionary Returns string.
>>> help(odbchelper, 30)
buildConnectionString          Build a connection string from a dictionary Returns string.
>>> help(odbchelper, 30, 0)
buildConnectionString          Build a connection string from a dictionary
    
    Returns string.

2.8. Объединяем все вместе

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

Пример 2.25. Основная часть apihelper.py

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

Обратите внимание, что это одна команда разбитая на несколько строк без явного использования признака продолжения (“\”). Помните, я говорил, что некоторые выражения могут быть записаны в несколько строк без использования обратного слэша? Расширенная запись списков — один из таких случаев, так как все выражение заключено в квадратные скобки.

Давайте разберем его с конца. Фраза


for method in methodList

говорит о том, ято мы имеем дело с расширенной записью списков. Как вы уже знаете, methodList является списком всех интересующих нас методов объекта object. Таким образом мы пробегаем по списку методов и каждый метод доступен через переменную method.

Пример 2.26. Динамическое извлечение строки документации

>>> import odbchelper
>>> object = odbchelper                   1
>>> method = 'buildConnectionString'      2
>>> getattr(object, method)               3
<function buildConnectionString at 010D6D74>
>>> print getattr(object, method).__doc__ 4
Создает и возвращает строку соединения из словаря параметров.
1 В функцию help object — объект, для которого мы хотим получить помощь — передается в качестве аргумента.
2 По ходу того, как мы пробегаем по списку methodList, method содержит имя текущего метода.
3 С помощью функции getattr мы получаем сам метод method объекта object.
4 Теперь осталось самое простое — распечатать строку документации метода.

Следующий кирпичик — использование функции str. Как вы уже знаете, str — встроенная функция, перобразующая объект к строке. Но строка документации уже является строкой, так зачем же суетиться с использованием функции str? На самом деле не каждая функция имеет строку документации, и если строки документации нет, ее атрибут __doc__ равен None.

Пример 2.27. Зачем использовать str для строк документации?

>>> {}.keys.__doc__         1
>>> {}.keys.__doc__ == None 2
1
>>> str({}.keys.__doc__)    3
'None'
1 Метод keys словаря не имеет строки документации, так что его атрибут __doc__ равен None. Если вы набираете выражение для атрибута __doc__ непосредственно в интерактивном режиме, интерпретатор Python ничего не выводит, что может сбить с толку (такое поведение интерпретатора действительно удобно, если впомнить о процедурах, которые в языке Python возвращают None).
2 Вы можете убедиться, что атрибут __doc__ действительно равен None простым сравнением.
3 Функция str возвращает строковое представление объекта, в данном случае 'None'.
Замечание
В SQL вы должны использовать IS NULL вместо = NULL для сравнения с пустым значением. В языке Python вы можете использовать как == None, так и is None, но второй вариант работает быстрее.

Теперь, когда мы уверены в том, что значение всегда будет строковым, можно передать его ранее определенной функции processFunc, которая сворачивает символы пропуска или возвращает строку без изменений. Теперь вы видите, почему важно использовать функцию str для преобразования None к строке. processFunc считает, что ее аргумент является строкой и вызывает его метод split. При попытке вызвать processFunc для None будет сгенерировано исключение, так как у None нет метода split.

Далее мы снова используем форматирование, чтобы соединить значение, возвращаемое функцией processFunc, со результатом применения метода ljust к строке method. Это новый метод строковых объектов, который мы раньше не видели.

Пример 2.28. Метод ljust

>>> s = 'buildConnectionString'
>>> s.ljust(30) 1
'buildConnectionString         '
>>> s.ljust(20) 2
'buildConnectionString'
1 ljust дополняет строку пробелами до указанной длины. Именно это делает функция help при выводе в две колонки для выравнивания строк документации во второй колонке.
2 Если требуемая длина меньше, чем исходная длина строки, ljust вернет строку без изменений. Этот метод никогда не обрезает строку.

Ну вот и почти все сделано. Имея метод ljust, дополняющий имя метода пробелами до нужной длины, и строку документации (возможно со свернутыми символами пропуска), которую возвращает processFunc, мы объединяем их и получаем одну строку. Так как мы пробегаем по списку methodList, то получим список строк. Используя метод join строки "\n", мы объединяем строки в одну с символом перехода на новую строку в качестве разделителя и печатаем результат.

Пример 2.29. Печать списка

>>> li = ['a', 'b', 'c']
>>> print "\n".join(li) 1
a
b
c
1 Этот прием также будет будет полезен при отладке для вывода списков. А в языке Python вы будете работать со списками постоянно.

Теперь код обрел смысл.

Пример 2.30. Основная часть apihelper.py, пересмотренная

    print "\n".join(["%s %s" %
                      (method.ljust(spacing),
                       processFunc(str(getattr(object, method).__doc__)))
                     for method in methodList])

2.2. Необязательные и именованные аргументы

В языке Python аргументы функции могут иметь значения по умолчанию, оно будет использовано, если при вызове функции значение этого аргумента не указано. Более того, аргументы имеющие значение по умолчанию при вызове могут быть указаны в произвольном порядке, если указано имя аргумента. В хранимых процедурах для SQL сервера Transact/SQL также могут быть использованы именованные аргументы; если вы пишите сценарии для SQL сервера, можете лишь бегло ознакомиться с этим разделом.

Пример 2.4. help — функция с двумя необязательными аргументами


def help(object, spacing=10, collapse=1):

Аргументы spacing и collapse могут быть опущены при вызове, так как для них определены значения по умолчанию. Аргумент object не имеет значения по умолчанию, поэтому должен быть указан всегда. Если функция help вызывается только с одним аргументом, то spacing принимает значение 10 и collapse принимает значение 1. Если help вызывается с двумя аргументами, collapse также принимает значение 1.

Представьте теперь, что вы хотите указать значение аргумента collapse, но использовать значение по умолчанию для spacing. В большинстве языков вам придется передавать все три аргумента. Однако Python позволяет передавать аргументы в произвольном порядке по имени.

Пример 2.5. Возможные способы вызова help

help(odbchelper)                    1
help(odbchelper, 12)                2
help(odbchelper, collapse=0)        3
help(spacing=15, object=odbchelper) 4
1 С одним аргументом, spacing и collapse получают значения по умолчанию, 10 и 1 соответственно.
2 С двумя аргументами, collapse получает значение по умолчанию 1.
3 Здесь вы явно указываете имя аргумента, collapse, для которого передается значение. Аргумент spacing по-прежнему получает значение по умолчанию 10.
4 Даже обязательные аргументы (в данном случае — object), которые не имеют значения по умолчанию, могут быть переданы по имени, и именованные аргументы могут быть указаны в произвольном порядке.

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

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

Дополнительная литература


2.3. type, str, dir и другие встроенные функции

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

Функция type возвращает тип произвольного объекта. Возможные значения типов перечислены в модуле types. Определение типа полезно в функциях, способных обрабатывать данные нескольких типов.

Пример 2.6. Функция type

>>> type(1)           1
<type 'int'>
>>> li = []
>>> type(li)          2
<type 'list'>
>>> import odbchelper
>>> type(odbchelper)  3
<type 'module'>
>>> import types      4
>>> type(odbchelper) == types.ModuleType
1
1 Функция type воспринимает объект произвольного типа и возвращает его тип. Аргумент действительно может быть произвольного типа: число, строка, список, словарь, кортеж, функция, класс, модуль и даже сам тип.
2 В качестве аргумента может использоваться переменная.
3 Функция type работает и c модулей.
4 Вы можете использовать константы, определенные в модуле types для сравнения типов объектов. Именно это и делает, как мы скоро увидим, функция help.

Функция str преобразует данные в строку. Для любого типа данных можно получить строковое представление.

Пример 2.7. Функция str

>>> str(1)          1
'1'
>>> horsemen = ['war', 'pestilence', 'famine']
>>> horsemen.append('Powerbuilder')
>>> str(horsemen)   2
"['war', 'pestilence', 'famine', 'Powerbuilder']"
>>> str(odbchelper) 3
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>"
>>> str(None)       4
'None'
1 Вы, наверное, ожидали, что str будет работать с такими простыми типами данных, как целые числа, так как почти каждый язык содержит функцию для преобразования целого числа в строку.
2 Однако, str работает и с объектами других типов. В данном случае мы получаем строковое представление списка.
3 Функция str работает и с модулями. Обратите внимание, что строковое представление модуля содержит путь к модулю на диске, так что в вашем случае он будет отличаться.
4 Важно также, что str работает и с объектом None, “нулевым” значением в языке Python. В этом случае она возвращает строку 'None'. Мы воспользуемся этим в функции help.

Сердцем функции help является мощная функция dir. dir возвращает список атрибутов и методов произвольного объекта: модуля, функции, строки, списка, словаря… в общем, любого объекта.

Пример 2.8. Функция dir

>>> li = []
>>> dir(li)           1
['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
>>> d = {}
>>> dir(d)            2
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values']
>>> import odbchelper
>>> dir(odbchelper)   3
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
1 li является списком, так что dir(li) дает список имен всех методов списка. Обратите внимание, что возвращаемый список содержит имена методов в виде строк, а не сами методы.
2 d является словарем, поэтому dir(d) дает список имен методов словаря. Как минимум один из них, keys, вам уже знаком.
3 Здесь начинается самое интересное. odbchelper является модулем, так что dir(odbchelper) дает список всех имен, определенных в модуле, включая специальные атрибуты, такие как __name__ и __doc__. В данном случае, odbchelper содержит одну пользовательскую функцию, buildConnectionString, которую мы изучали в главе Знакомство с языком Python.

Наконец, функция callable возвращает 1, если аргумент может быть вызван, в противном случае возвращает 0. Вызов поддерживают такие объекты, как функции, методы и даже классы. (Более подробно о классах читайте в главе 3.)

Пример 2.9. Функция callable

>>> import string
>>> string.punctuation           1
'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
>>> string.join                  2
<function join at 00C55A7C>
>>> callable(string.punctuation) 3
0
>>> callable(string.join)        4
1
>>> print string.join.__doc__    5
join(list [,sep]) -> string

    Return a string composed of the words in list, with
    intervening occurrences of sep.  The default separator is a
    single space.

    (joinfields and join are synonymous)
1 На смену большинству функций модуля string пришли методов строк (хотя многие до сих пор используют функцию join), но модуль также содержит множество полезных констант. Например string.punctuation содержит все стандартные символы пунктуации.
2 Функция string.join объединяет строки списка в одну строку.
3 string.punctuation является строкой и вызов не поддерживает. (Строки имеют методы, которые можно вызвать, но вы не можете вызвать саму строку.)
4 string.join ялвляется функцией и поддерживает вызов с двумя аргументами.
5 Любой объект, поддерживающий вызов, может иметь строку документации. Применяя функцию callable к каждому атрибуту объекта, мы можем определить, какие атрибуты представляют для нас интерес (методы, функции, классы), и какие можно игнорировать (константы и т. д.), не имея каких-либо начальных знаний об объекте.

type, str, dir и другие втроенные функции языка Python сгруппированы в специальном модуле __builtin__ (обратите внимание на два символа подчеркивания в начале и два в конце). Встроенные функции доступны напрямую, как если бы интерпретатор при запуске автоматически выполнял from __builtin__ import *.

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

Пример 2.10. Втроенные функции и другие объекты

>>> from apihelper import help
>>> import __builtin__
>>> help(__builtin__, 20)
ArithmeticError      Base class for arithmetic errors.
AssertionError       Assertion failed.
AttributeError       Attribute not found.
EOFError             Read beyond end of file.
EnvironmentError     Base class for I/O related errors.
Exception            Common base class for all exceptions.
FloatingPointError   Floating point operation failed.
IOError              I/O operation failed.

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

Дополнительная литература


2.4. Получение атрибутов с помощью getattr

Вы уже знаете, что функции в языке Python являются объектами. Но вы пока не знаете, что если имя функции становится известно только во время выполнения программы, то ее можно получить с помощью функции getattr.

Пример 2.11. Функция getattr

>>> li = ["Larry", "Curly"]
>>> li.pop                       1
<built-in method pop of list object at 010DF884>
>>> getattr(li, "pop")           2
<built-in method pop of list object at 010DF884>
>>> getattr(li, "append")("Moe") 3
>>> li
["Larry", "Curly", "Moe"]
>>> getattr({}, "clear")         4
<built-in method clear of dictionary object at 00F113D4>
>>> getattr((), "pop")           5
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'tuple' object has no attribute 'pop'
1 Таким образом вы получаете метод pop списка. Обратите внимание, что вы не вызываете его (для этого нужно выполнить li.pop()), а получаете в виде объекта.
2 Здесь мы также получаем метод pop, но теперь имя метода указано в виде строкового аргумента функции getattr. getattr — очень полезная функция, позволяющая получить любой атрибут любого объекта. В данном случае объектом является список, а его атрибутом — метод pop.
3 В случае, если вы еще не до конца осознали, насколько это может быть полезным, попробуйте выполнить этот код: значение, возвращаемое функцией getattr является методом, который можно вызвать, как если бы вы просто вызвали li.append("Moe"). Но вы не вызываете метод напрямую — вы указываете имя метода в виде строки.
4 getattr работает и для словарей.
5 Теоретически, getattr работает и для кортежей, но кортежи не имеют методов, так что getattr сгенерирует исключение независимо от имени атрибута, которое вы дадите.

getattr предназначен не только для встроенных типов данных. Он также работает и для модулей.

Пример 2.12. getattr в apihelper.py

>>> import odbchelper
>>> odbchelper.buildConnectionString             1
<function buildConnectionString at 00D18DD4>
>>> getattr(odbchelper, "buildConnectionString") 2
<function buildConnectionString at 00D18DD4>
>>> object = odbchelper
>>> method = "buildConnectionString"
>>> getattr(object, method)                      3
<function buildConnectionString at 00D18DD4>
>>> type(getattr(object, method))                4
<type 'function'>
>>> import types
>>> type(getattr(object, method)) == types.FunctionType
1
>>> callable(getattr(object, method))            5
1
1 Так мы можем получить функцию buildConnectionString из модуля odbchelper, который сы изучали в разделе Знакомство с языком Python. (Шестнадцатиричный адрес характерен для моей машины, у вас он будет другим.)
2 Используя getattr, мы можем получить ту же самую функцию. В общем, getattr(object, "attribute") дает такой же результат, как object.attribute. Если object является модулем, attribute может быть любым объектом, определенном в этом модуле: функцией, классом или другой глобальной переменной.
3 На самом деле именно это и делает функция help. object передается функции в качестве аргумента; method является строкой с именем метода или функции.
4 В данном случае, method содержит имя функции, в чем мы можем убедиться получив ее тип.
5 Так как method является функцией, ее можно вызвать.

2.5. Фильтрование списков

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

Пример 2.13. Синтаксис фильтрования списков

[mapping-expression for element in source-list if filter-expression]

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

Пример 2.14. Введение в фильтрование списков

>>> li = ["a", "mpilgrim", "foo", "b", "c", "b", "d", "d"]
>>> [elem for elem in li if len(elem) > 1]       1
['mpilgrim', 'foo']
>>> [elem for elem in li if elem != "b"]         2
['a', 'mpilgrim', 'foo', 'c', 'd', 'd']
>>> [elem for elem in li if li.count(elem) == 1] 3
['a', 'mpilgrim', 'foo', 'c']
1 В данном случае выражение отображения совсем простое (значение каждого элемента), так что сосредоточьтесь на условии фильтра. Каждый элемент пробегаемого списка Python пропускает через фильтр. Если условие фильтра дает истину, элемент участвует в преобразовании и результат включается в возвращаемый список. В данном случае мы исключили все строки длиной в один символ.
2 Здесь мы исключаем элементы с одним определенным значением, "b". Обратите внимание, что фильтром отбрасываются все элементы со значением "b", так как во всех случаях выражение условия будет давать ложь.
3 Метод count списка возвращает количество вхождений элементов определенного значения в список. Вы можете подумать, что этот фильтр исключает все дубликаты, и возвращаемый список будет содержать по одному значению исходного списка. Но это не так, потому что значения, входящие в исходный список дважды (в данном случае это, "b" and "d") полностью исключаются. Существует множество способов исключить дубликаты из списка, но не так.

Пример 2.15. Фильтрование списка в apihelper.py

    methodList = [method for method in dir(object) if callable(getattr(object, method))]

Этот пример выглядит сложным, но основная структура остается прежней. Все выражение дает список, который присваивается переменной methodList. Выражение отображения простое: оно дает значение каждого элемента. Функция dir(object) возвращает список всех атрибутов и методов объекта object — это тот список, который мы преобразуем. И единственная новая часть — это условие фильтра после if.

Выражение фильтра выглядит жутко, но он таковым не является. Вы уже знаете о callable, getattr и in. Как вы могли видеть в предыдущем разделе, выражение getattr(object, method) дает объект-функцию, если object является модулем и method содержит имя функции из этого модуля.

Таким образом, мы берем объект object, получаем список имен его атрибутов, методов, функций, и затем фильтруем этот список, избавляясь от всего, что нас не интересует. Для того, чтобы избавиться от ненужного, мы берем имя каждого атрибута/метода/функции, с помощью функции getattr получаем настоящий объект. Затем мы проверяем, поддерживает ли объект вызов, таким образом подхватывая все функции и методы — как встроенные (например, метод pop списков), так и определенные пользователем (например, функция buildConnectionString в модуле odbchelper). Нас не интересуют другие атрибуты, такие как обязательный для всех моделей атрибут __name__.

Дополнительная литература


2.6. Особенности операторов and и or

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

Пример 2.16. Оператор and

>>> 'a' and 'b'         1
'b'
>>> '' and 'b'          2
''
>>> 'a' and 'b' and 'c' 3
'c'
1 При использовании оператора and, значения вычисляются в булевом контексте слева напрво. Значения 0, '', [], (), {} и None являются ложью, все остальное является истиной[3]. Если у and оба операнда являются истиной, результатом будет последнее значение. В данном случае вычисляется выражение 'a', которое является истиной, затем 'b', которое также является истиной, и возвращается 'b'.
2 Если какой-либо из операндов является ложью, результатом будет первое такое значение. В данном случает '' — первое значение, являющееся ложью.
3 Все значения являются истиной, так что в результате мы получаем последнее — 'c'.

Пример 2.17. Оператор or

>>> 'a' or 'b'          1
'a'
>>> '' or 'b'           2
'b'
>>> '' or [] or {}      3
{}
>>> def sidefx():
...     print "in sidefx()"
...     return 1
>>> 'a' or sidefx()     4
'a'
1 Как и для and опреранды or вычисляются в булевском контексте слева направо. Если операнд является истиной, or немедленно возвращает результат. В данном случае 'a' — первое истинное значение.
2 or вычисляет выражение '', которое является ложью, затем 'b', которое является истиной, и возвращает 'b'.
3 Если все значения являются ложью, or возвращает последнее. or вычисляет '' (ложь), [] (ложь), затем {} (ложь) и возвращает {}.
4 Обратите внимание, что or вычисляет операнды до тех пор, пока не найдет истинное значение, остальное игнорируется. Это имеет значение, когда вычисление операнда дает сторонние эффекты. В данном случае функция sidefx не вызывается, так как для получения результата выражения с опереатором or достаточно того, что первый операнд, 'a', является истиной.

Если вы используете C, то, наверное, знакомы с выражением bool ? a : b, которое дает a, если bool является истиной, b, если bool ложно. Благодаря особенностям работы операторов and и or в Python, вы можете достигнуть анологичного эффекта.

Пример 2.18. Прием с and-or

>>> a = "first"
>>> b = "second"
>>> 1 and a or b 1
'first'
>>> 0 and a or b 2
'second'
1 Синтаксис bool ? a : b в языке Python выглядит похоже. Все выражение вычисляется слева направо, так что оператор and применяется первым. 1 and 'first' дает 'first', затем 'first' or 'second' дает 'first'.
2 0 and 'first' дает 0, тогда 0 or 'second' дает 'second'.

Однако, так как такое выражение выполняет обычные логические операции, а не является специальной конструкцией языка, существует очень важная разница между приемом с and-or в языке Python и конструкцией bool ? a : b в C. Если значение a является ложью, это прием не будет работать так как вы могли ожидать. (Вы можете сказать, что обожгись на этом? Более одного раза?)

Пример 2.19. Когда прием с and-or не работает

>>> a = ""
>>> b = "second"
>>> 1 and a or b 1
'second'
1 Так как a — пустая строка, которую Python считает ложью в булевом контексте, 1 and '' дает '', а '' or 'second' дает 'second'. Ой! Это не то, что мы хотели получить.
Важно
Прием с and-or, bool and a or b, не будет работать так же, как конструкция bool ? a : b в C, если a является ложью.

Для безопасного использования приема с and-or необходимо сделать так, чтобы a всегда было истинным. Один из самых распространенных способо это сделать — заменить a на [a] и b на [b], тогда первый элемент получаемого списка будет либо a, либо b.

Пример 2.20. Безопасное использование приема с and-or

>>> a = ""
>>> b = "second"
>>> (1 and [a] or [b])[0] 1
''
1 Так как выражение [a] является непустым списком, оно никогда не будет ложно. Даже если a равно 0, '' или другому значению, являющемуся ложью, список [a] всегда будет являться истиной, потому что содержит один элемент.

Кажется, этот прием не стоит того, чтобы его использовать. В конце концов вы всегда можете воспользоваться интсрукцией if. Тогда зачем вся эта суета? Конечно, в большинстве случаев выбирая между двумя константами вы можете использовать простой синтаксис и не беспокоиться, потому что значение a всегда будет истинным. И даже если придется использовать более сложный синтакс, на это могут найтись весские причины: в некоторых случаях в языке Python использование инструкции if не допускается, например в lambda-функциях.

Дополнительная литература

Footnotes

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


2.7. Использование lambda-функций

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

Пример 2.21. lambda-функции

>>> def f(x):
...     return x*2
...     
>>> f(3)
6
>>> g = lambda x: x*2  1
>>> g(3)
6
>>> (lambda x: x*2)(3) 2
6
1 Эта lambda-функция делает то же самое, что и обычная функция, определенная выше. Обратите внимание на сокращенный синтаксис: список аргументов записывается без скобок и ключевое слово return отсутствует (оно подразумевается, так как тело функции может содержать только одно выражение). Кроме того, функция не имеет имени, но может быть вызвана через переменную, которой она присвоена.
2 Вы можете использовать lambda-функцию даже не присваивая ее переменной. Это не самый полезный пример, но он показывает, что lambda-функция может быть определена прямо в месте ее использования.

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

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

Пример 2.22. lambda-функции в apihelper.py

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

Здесь следует обратить внимание на несколько вещей. Во-первых, мы используем простую форму приема с and-or. В данном случае это оправданно, так как lambda-функция всегда является истиной в булевом контексте (но это не означает, что lambda-функция не может возвращать значение, являющееся ложью; функция всегда является истиной, не зависимо от возвращаемого значения).

Во-вторых, мы используем метод split без аргументов. Вы уже видели его использование с одним и двумя аргументами, без аргументов метод split разбивает строку по символам пропуска (пробел, табуляция, возврат коретки, переход на новую строку).

Пример 2.23. split без аргументов

>>> s = "this   is\na\ttest"  1
>>> print s
this   is
a	test
>>> print s.split()           2
['this', 'is', 'a', 'test']
>>> print " ".join(s.split()) 3
'this is a test'
1 Это строка, которая содержит символ переход на новую строку, записанный в виде специальной последовательности (такие строки могут быть также записаны с использованием утроенных кавычек). \n — переход на новую строку, \t — символ горизонтальной табуляции.
2 Метод split без аргументов разбивает строку по символам пропуска. В данном случае три пробела, переход на новую строку и табуляция воспринимаются одинаково.
3 Вы можете нормализовать пропуски разбив строку, а затем снова объединив ее, используя один пробул в качестве разделителя. Именно это делает функция help для того, чтобы свернуть строку документации.

Так что же на самом деле делает функция help с этими lambda-функциями, методами split и приемом с and-or?

Пример 2.24. Присваивание функции переменной

    processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s)

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

Для того, чтобы сделать это на менее мощном языке, например на Visual Basic, вы, скорее всего, будете использовать интсрукцию if, чтобы решить, сворачивать символы пропуска или нет. Такой подход неэффективен, так как проверять условие придется при каждом вызове функции. В языке Python вы можете принять решение один раз и определить lambda-функцию, которая будет делать только то, что вам нужно.

Дополнительная литература


3.3. Определение классов

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

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

Пример 3.5. Простейший класс


class foo: 1
    pass   2 3
1 Определяем класс с именем foo, который не является производным от других классов.
2 Этот класс не определяет никаких методов и атрибутов, но синтаксис требует наличие хотябы одной иструкции в определении, поэтому мы использовали pass. Это зарезервированное слово, которое означает, что ничего делать не нужно. Инструкция pass ничего не делает и полезна в качестве наполнителя в случаях, когда вы хотите поставить заглушку в определении функции или класса.
3 Как вы, наверное, уже догадались, тело класса записывается с отступом, как и тело функции или инструкций if, for и т. д. Первая строка, записанная без отступа, уже не попадает в определение класса.
Замечание
Инструкция pass в Python ведет себя аналогично пустым фигурным скобкам ({}) в Java и C.

Конечно, в реальных программах большинство классов будут производными от других классов и будут определять собственные атрибуты и методы. Но, как вы уже увидели, нет ничего, что класс обязательно должен иметь, кроме имени. В частности, программистам на C++ может показаться странным, что классы в языке Python не имеют явных конструкторов и деструкторов. В классах языка Python есть нечто? похожее на конструктор — метод __init__.

Пример 3.6. Определение класса FileInfo


from UserDict import UserDict

class FileInfo(UserDict): 1
1 В языке Python родительские классы просто перечисляются в скобках сразу после имени класса. В данном случае класс FileInfo наследуется от класса UserDict (который был проимпортирован из модуля UserDict). UserDict — класс, который ведет себя аналогично словарю, позволяя от него наследоваться и изменять или дополнять его поведение. (Существуют аналогичные классы UserList и UserString, позволяющие определить класс, производный от списка и строки.) Здесь есть немного черной магии, которую мы раскроем позже в этой главе, когда будем подробнее исследовать класс UserDict.
Замечание
В языке Python родительские классы просто перечисляются в скобках после имени. Для этого не нужно использовать специальное ключевое слово, такое как extends в Java.
Замечание
Хотя я не буду буду рассказывать об этом подробно, Python поддерживает множественное наследование. В скобках после имени класса вы можете пересислить через запятую столько родительских классов, сколько вам нужно.

Пример 3.7. Инициализация класса FileInfo


class FileInfo(UserDict):
    "хранит метаинформацию о файле"    1
    def __init__(self, filename=None): 2 3 4
1 Для классов можно (и желательно) определять строку документации, также как для модулей и функций.
2 Метод __init__ вызывается сразу после создания экземпляра класса. Соблазнительно, но не правильно называть этот метод конструктором. Соблазнительно, потому что он выглядит как конструктор (принято, чтобы __init__ был первым методом, определенным в классе), ведет себя как коструктор (это перый кусок кода, вызываемый в созданном экземпляре класса) и даже называется как коструктор. Неправильно, так как к тому времени, когда вызывается метод __init__, объект уже создан и вы имеете ссылку на созданный экземпляр класса. Но метод __init__ — это самое близкое к конструктору, из того что есть в языке Python.
3 Первым аргументом каждого метода класса, включая __init__, всегда является текущий экземпляр класса. Общепринято всегда называть этот аргумент self. В методе __init__ self ссылается на только что созданный объект, в других методах — на экземпляр класса, для которого метод вызывается. Хотя и необходимо явно указывать self при определении метода, вы его не указываете, когда вызываете метод; Python добавит его автоматически.
4 Метод __init__ может иметь несколько аргументов. Аргументы могут иметь значения по умолчанию, что сделает их необязательными. В данном случае аргумент filename имеет значение по умолчанию None.
Замечание
Первый аргумент метода класса (ссылка на текущий экземпляр) принято называть self. Этот аргумент играет роль зарезервированного слова this в C++ и Java, но self не является зарезервированным словом — просто соглашение. Несмотря на это, не стоит называть его иначе, чем self.

Пример 3.8. Тело класса FileInfo


class FileInfo(UserDict):
    "хранит метаинформацию о файле"   
    def __init__(self, filename=None):
        UserDict.__init__(self)        1
        self["name"] = filename        2
                                       3
1 Некоторыми псевдо-объектно-ориентированными языками, например Powerbuilder, поддерживается концепция “расширения” конструкторов и других обработчиков событий: метод базового класса автоматически вызывается перед выполнением метода производного класса. В Python этого не происходит, необходимо явно вызывать метод в производном классе.
2 Я сказал, что наш класс ведет себя аналогично словарю, и вот первое проявление такого поведения: мы присваивает значение аргумента filename записи объекта с ключом name.
3 Обратите внимание, что метод __init__ никогда не возвращает значение.
Замечание
В определении методов необходимо явно указывать self в качестве первого аргумента любого метода, включая __init__. При вызове метода базового класса также необходимо включать self в список аргументов. Но при вызове метода извне аргумент self не указывается, а подставляется интерпретатором автоматически. I am aware that this is confusing at first; it's not really inconsistent, but it may appear inconsistent because it relies on a distinction (between bound and unbound methods) that you don't know about yet.

Whew. I realize that's a lot to absorb, but you'll get the hang of it. All Python classes work the same way, so once you learn one, you've learned them all. If you forget everything else, remember this one thing, because I promise it will trip you up:

Замечание
__init__ methods are optional, but when you define one, you must remember to explicitly call the ancestor's __init__ method. This is more generally true: whenever a descendant wants to extend the behavior of the ancestor, the descendant method must explicitly call the ancestor method at the proper time, with the proper arguments.

Further reading


3.2. Импортирование модулей инструкцией from module import

В языке Python есть два способа импортировать модули. Оба из них полезны, и вы должны знать, когда каждый из них лучше использовать. С одним способом, import module, вы уже ознакомились в главе 1. Второй способ делает примерно то же самое, но в его работе есть несколько важных отличий.

Пример 3.3. Синтаксис from module import


from UserDict import UserDict

Синтакис очень похож на уже знакомый вам ситаксис import module, но есть одно важное отличие: атрибуты модуля types импортируются непосредственно в локальное пространство имен и, поэтому, становятся доступными напрямую, без указания имени модуля.

Замечание
from module import in Python is like use module in Perl; import module in Python is like require module in Perl.
Замечание
from module import in Python is like import module.* in Java; import module in Python is like import module in Java.

Пример 3.4. import module vs. from module import

>>> import types
>>> types.FunctionType             1
<type 'function'>
>>> FunctionType                   2
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
NameError: There is no variable named 'FunctionType'
>>> from types import FunctionType 3
>>> FunctionType                   4
<type 'function'>
1 Модуль types содержит объекты-типы для объектов всех встроенных типов языка Python. Обратите внимание, что имя объекта, FunctionType, должно быть указано с использованием имени модуля, types.
2 Имя FunctionType не определено в этом пространстве имен, оно доступно только в контексте types.
3 Используя такой синтаксис вы импортируете FunctionType из модуля types непосредственно в локальное пространство имен.
4 Теперь объект FunctionType доступен напрямую, без указания имени модуля.

Когда следует использовать from module import?

Во всех остальных случаях — это дело вкуса. Вы увидите код на языке Python с использованием обоих вариантов.

Дополнительная литература


3.4. Instantiating classes

Instantiating classes in Python is straightforward. To instantiate a class, simply call the class as if it were a function, passing the arguments that the __init__ method defines. The return value will be the newly created object.

Пример 3.9. Creating a FileInfo instance

>>> import fileinfo
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3") 1
>>> f.__class__                                        2
<class fileinfo.FileInfo at 010EC204>
>>> f.__doc__                                          3
'base class for file info'
>>> f                                                  4
{'name': '/music/_singles/kairo.mp3'}
1 We are creating an instance of the FileInfo class (defined in the fileinfo module) and assigning the newly created instance to the variable f. We are passing one parameter, /music/_singles/kairo.mp3, which will end up as the filename argument in FileInfo's __init__ method.
2 Every class instance has a built-in attribute, __class__, which is the object's class. (Note that the representation of this includes the physical address of the instance on my machine; your representation will be different.) Java programmers may be familiar with the Class class, which contains methods like getName and getSuperclass to get metadata information about an object. In Python, this kind of metadata is available directly on the object itself through attributes like __class__, __name__, and __bases__.
3 You can access the instance's doc string just like a function or a module. All instances of a class share the same doc string.
4 Remember when the __init__ method assigned its filename argument to self["name"]? Well, here's the result. The arguments we pass when we create the class instance get sent right along to the __init__ method (along with the object reference, self, which Python adds for free).
Замечание
In Python, simply call a class as if it were a function to create a new instance of the class. There is no explicit new operator like C++ or Java.

If creating new instances is easy, destroying them is even easier. In general, there is no need to explicitly free instances, because they are freed automatically when the variables assigned to them go out of scope. Memory leaks are rare in Python.

Пример 3.10. Trying to implement a memory leak

>>> def leakmem():
...     f = fileinfo.FileInfo('/music/_singles/kairo.mp3') 1
...     
>>> for i in range(100):
...     leakmem()                                          2
1 Every time the leakmem function is called, we are creating an instance of FileInfo and assigning it to the variable f, which is a local variable within the function. Then the function ends without ever freeing f, so you would expect a memory leak, but you would be wrong. When the function ends, the local variable f goes out of scope. At this point, there are no longer any references to the newly created instance of FileInfo (since we never assigned it to anything other than f), so Python destroys the instance for us.
2 No matter how many times we call the leakmem function, it will never leak memory, because every time, Python will destroy the newly created FileInfo class before returning from leakmem.

The technical term for this form of garbage collection is “reference counting”. Python keeps a list of references to every instance created. In the above example, there was only one reference to the FileInfo instance: the local variable f. When the function ends, the variable f goes out of scope, so the reference count drops to 0, and Python destroys the instance automatically.

In previous versions of Python, there were situations where reference counting failed, and Python couldn't clean up after you. If you created two instances that referenced each other (for instance, a doubly-linked list, where each node has a pointer to the previous and next node in the list), neither instance would ever be destroyed automatically because Python (correctly) believed that there is always a reference to each instance. Python 2.0 has an additional form of garbage collection called “mark-and-sweep” which is smart enough to notice this virtual gridlock and clean up circular references correctly.

As a former philosophy major, it disturbs me to think that things disappear when no one is looking at them, but that's exactly what happens in Python. In general, you can simply forget about memory management and let Python clean up after you.

Further reading


3.5. UserDict: a wrapper class

As you've seen, FileInfo is a class that acts like a dictionary. To explore this further, let's look at the UserDict class in the UserDict module, which is the ancestor of our FileInfo class. This is nothing special; the class is written in Python and stored in a .py file, just like our code. In particular, it's stored in the lib directory in your Python installation.

Подсказка
In the Python IDE on Windows, you can quickly open any module in your library path with File->Locate... (Ctrl-L).

In Python, you can not subclass built-in datatypes like strings, lists, and dictionaries. To compensate for this, Python comes with wrapper classes that mimic the behavior of these built-in datatypes: UserString, UserList, and UserDict. Using a combination of normal and special methods, the UserDict class does an excellent imitation of a dictionary, but it's just a class like any other, so you can subclass it to provide custom dictionary-like classes like FileInfo.

Пример 3.11. Defining the UserDict class


class UserDict:                                1
    def __init__(self, dict=None):             2
        self.data = {}                         3
        if dict is not None: self.update(dict) 4
1 Note that UserDict is a base class, not inherited from any other class.
2 This is the __init__ method that we overrode in the FileInfo class. Note that the argument list in this ancestor class is different than the descendant. That's okay; each subclass can have its own set of arguments, as long as it calls the ancestor with the correct arguments. Here the ancestor class has a way to define initial values (by passing a dictionary in the dict argument) which our FileInfo does not take advantage of.
3 Python supports data attributes (called “instance variables” in Java and Powerbuilder, “member variables” in C++), which is data held by a specific instance of a class. In this case, each instance of UserDict will have a data attribute data. To reference this attribute from code outside the class, you would qualify it with the instance name, instance.data, in the same way that you qualify a function with its module name. To reference a data attribute from within the class, we use self as the qualifier. By convention, all data attributes are initialized to reasonable values in the __init__ method. However, this is not required, since data attributes, like local variables, spring into existence when they are first assigned a value.
4 This is a syntax you may not have seen before (I haven't used it in the examples in this book). This is an if statement, but instead of having an indented block starting on the next line, there is just a single statement on the same line, after the colon. This is perfectly legal syntax, and is just a shortcut when you have only one statement in a block. (It's like specifying a single statement without braces in C++.) You can use this syntax, or you can have indented code on subsequent lines, but you can't do both for the same block.
Замечание
Java and Powerbuilder support function overloading by argument list, i.e. one class can have multiple methods with the same name but a different number of arguments, or arguments of different types. Other languages (most notably PL/SQL) even support function overloading by argument name; i.e. one class can have multiple methods with the same name and the same number of arguments of the same type but different argument names. Python supports neither of these; it has no form of function overloading whatsoever. An __init__ method is an __init__ method is an __init__ method, regardless of its arguments. There can be only one __init__ method per class, and if a descendant class has an __init__ method, it always overrides the ancestor __init__ method, even if the descendant defines it with a different argument list.
Замечание
Always assign an initial value to all of an instance's data attributes in the __init__ method. It will save you hours of debugging later.

Пример 3.12. UserDict normal methods

    def clear(self): self.data.clear()          1
    def copy(self):                             2
        if self.__class__ is UserDict:          3
            return UserDict(self.data)         
        import copy                             4
        return copy.copy(self)                 
    def keys(self): return self.data.keys()     5
    def items(self): return self.data.items()  
    def values(self): return self.data.values()
1 clear is a normal class method; it is publicly available to be called by anyone at any time. Note that clear, like all class methods, has self as its first argument. (Remember, you don't include self when you call the method; it's something that Python adds for you.) Also note the basic technique of this wrapper class: store a real dictionary (data) as a data attribute, define all the methods that a real dictionary has, and have each class method redirect to the corresponding method on the real dictionary. (In case you'd forgotten, a dictionary's clear method deletes all of its keys and their associated values.)
2 The copy method of a real dictionary returns a new dictionary that is an exact duplicate of the original (all the same key-value pairs). But UserDict can't simply redirect to self.data.copy, because that method returns a real dictionary, and what we want is to return a new instance that is the same class as self.
3 We use the __class__ attribute to see if self is a UserDict; if so, we're golden, because we know how to copy a UserDict: just create a new UserDict and give it the real dictionary that we've squirreled away in self.data.
4 If self.__class__ is not UserDict, then self must be some subclass of UserDict (like maybe FileInfo), in which case life gets trickier. UserDict doesn't know how to make an exact copy of one of its descendants; there could, for instance, be other data attributes defined in the subclass, so we would have to iterate through them and make sure to copy all of them. Luckily, Python comes with a module to do exactly this, and it's called copy. I won't go into the details here (though it's a wicked cool module, if you're ever inclined to dive into it on your own). Suffice to say that copy can copy arbitrary Python objects, and that's how we're using it here.
5 The rest of the methods are straightforward, redirecting the calls to the built-in methods on self.data.

Further reading


3.6. Special class methods

In addition to normal class methods, there are a number of special methods which Python classes can define. Instead of being called directly by your code (like normal methods), special methods are called for you by Python in particular circumstances or when specific syntax is used.

As you saw in the previous section, normal methods went a long way towards wrapping a dictionary in a class. But normal methods alone are not enough, because there are lots of things you can do with dictionaries besides call methods on them. For starters, you can get and set items with a syntax that doesn't include explicitly invoking methods. This is where special class methods come in: they provide a way to map non-method-calling syntax into method calls.

Пример 3.13. The __getitem__ special method

    def __getitem__(self, key): return self.data[key]
>>> f = fileinfo.FileInfo("/music/_singles/kairo.mp3")
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__getitem__("name") 1
'/music/_singles/kairo.mp3'
>>> f["name"]             2
'/music/_singles/kairo.mp3'
1 The __getitem__ special method looks simple enough. Like the normal methods clear, keys, and values, it just redirects to the dictionary to return its value. But how does it get called? Well, you can call __getitem__ directly, but in practice you wouldn't actually do that; I'm just doing it here to show you how it works. The right way to use __getitem__ is to get Python to call it for you.
2 This looks just like the syntax you would use to get a dictionary value, and in fact it returns the value you would expect. But here's the missing link: under the covers, Python has converted this syntax to the method call f.__getitem__("name"). That's why __getitem__ is a special class method; not only can you call it yourself, you can get Python to call it for you by using the right syntax.

Пример 3.14. The __setitem__ special method

    def __setitem__(self, key, item): self.data[key] = item
>>> f
{'name':'/music/_singles/kairo.mp3'}
>>> f.__setitem__("genre", 31) 1
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':31}
>>> f["genre"] = 32            2
>>> f
{'name':'/music/_singles/kairo.mp3', 'genre':32}
1 Like the __getitem__ method, __setitem__ simply redirects to the real dictionary self.data to do its work. And like __getitem__, you wouldn't ordinarily call it directly like this; Python calls __setitem__ for you when you use the right syntax.
2 This looks like regular dictionary syntax, except of course that f is really a class that's trying very hard to masquerade as a dictionary, and __setitem__ is an essential part of that masquerade. This line of code actually calls f.__setitem__("genre", 32) under the covers.

__setitem__ is a special class method because it gets called for you, but it's still a class method. Just as easily as the __setitem__ method was defined in UserDict, we can redefine it in our descendant class to override the ancestor method. This allows us to define classes that act like dictionaries in some ways but define their own behavior above and beyond the built-in dictionary.

This concept is the basis of the entire framework we're studying in this chapter. Each file type can have a handler class which knows how to get metadata from a particular type of file. Once some attributes (like the file's name and location) are known, the handler class knows how to derive other attributes automatically. This is done by overriding the __setitem__ method, checking for particular keys, and adding additional processing when they are found.

For example, MP3FileInfo is a descendant of FileInfo. When an MP3FileInfo's name is set, it doesn't just set the name key (like the ancestor FileInfo does); it also looks in the file itself for MP3 tags and populates a whole set of keys.

Пример 3.15. Overriding __setitem__ in MP3FileInfo

    def __setitem__(self, key, item):         1
        if key == "name" and item:            2
            self.__parse(item)                3
        FileInfo.__setitem__(self, key, item) 4
1 Note that our __setitem__ method is defined exactly the same way as the ancestor method. This is important, since Python will be calling the method for us, and it expects it to be defined with a certain number of arguments. (Technically speaking, the names of the arguments don't matter, just the number.)
2 Here's the crux of the entire MP3FileInfo class: if we're assigning a value to the name key, we want to do something extra.
3 The extra processing we do for names is encapsulated in the __parse method. This is another class method defined in MP3FileInfo, and when we call it, we qualify it with self. Just calling __parse would look for a normal function defined outside the class, which is not what we want; calling self.__parse will look for a class method defined within the class. This isn't anything new; you reference data attributes the same way.
4 After doing our extra processing, we want to call the ancestor method. Remember, this is never done for you in Python; you have to do it manually. Note that we're calling the immediate ancestor, FileInfo, even though it doesn't have a __setitem__ method. That's okay, because Python will walk up the ancestor tree until it finds a class with the method we're calling, so this line of code will eventually find and call the __setitem__ defined in UserDict.
Замечание
When accessing data attributes within a class, you need to qualify the attribute name: self.attribute. When calling other methods within a class, you need to qualify the method name: self.method.

Пример 3.16. Setting an MP3FileInfo's name

>>> import fileinfo
>>> mp3file = fileinfo.MP3FileInfo()                   1
>>> mp3file
{'name':None}
>>> mp3file["name"] = "/music/_singles/kairo.mp3"      2
>>> mp3file
{'album': 'Rave Mix', 'artist': '***DJ MARY-JANE***', 'genre': 31,
'title': 'KAIRO****THE BEST GOA', 'name': '/music/_singles/kairo.mp3',
'year': '2000', 'comment': 'http://mp3.com/DJMARYJANE'}
>>> mp3file["name"] = "/music/_singles/sidewinder.mp3" 3
>>> mp3file
{'album': '', 'artist': 'The Cynic Project', 'genre': 18, 'title': 'Sidewinder', 
'name': '/music/_singles/sidewinder.mp3', 'year': '2000', 
'comment': 'http://mp3.com/cynicproject'}
1 First, we create an instance of MP3FileInfo, without passing it a filename. (We can get away with this because the filename argument of the __init__ method is optional.) Since MP3FileInfo has no __init__ method of its own, Python walks up the ancestor tree and finds the __init__ method of FileInfo. This __init__ method manually calls the __init__ method of UserDict and then sets the name key to filename, which is None, since we didn't pass a filename. Thus, mp3file initially looks like a dictionary with one key, name, whose value is None.
2 Now the real fun begins. Setting the name key of mp3file triggers the __setitem__ method on MP3FileInfo (not UserDict), which notices that we're setting the name key with a real value and calls self.__parse. Although we haven't traced through the __parse method yet, you can see from the output that it sets several other keys: album, artist, genre, title, year, and comment.
3 Modifying the name key will go through the same process again: Python calls __setitem__, which calls self.__parse, which sets all the other keys.

3.7. Advanced special class methods

There are more special methods than just __getitem__ and __setitem__. Some of them let you emulate functionality that you may not even know about.

Пример 3.17. More special methods in UserDict

    def __repr__(self): return repr(self.data)     1
    def __cmp__(self, dict):                       2
        if isinstance(dict, UserDict):            
            return cmp(self.data, dict.data)      
        else:                                     
            return cmp(self.data, dict)           
    def __len__(self): return len(self.data)       3
    def __delitem__(self, key): del self.data[key] 4
1 __repr__ is a special method which is called when you call repr(instance). The repr function is a built-in function that returns a string representation of an object. It works on any object, not just class instances. You're already intimately familiar with repr and you don't even know it. In the interactive window, when you type just a variable name and hit ENTER, Python uses repr to display the variable's value. Go create a dictionary d with some data and then print repr(d) to see for yourself.
2 __cmp__ is called when you compare class instances. In general, you can compare any two Python objects, not just class instances, by using ==. There are rules that define when built-in datatypes are considered equal; for instance, dictionaries are equal when they have all the same keys and values, and strings are equal when they are the same length and contain the same sequence of characters. For class instances, you can define the __cmp__ method and code the comparison logic yourself, and then you can use == to compare instances of your class and Python will call your __cmp__ special method for you.
3 __len__ is called when you call len(instance). The len function is a built-in function that returns the length of an object. It works on any object that could reasonably be thought of as having a length. The len of a string is its number of characters; the len of a dictionary is its number of keys; the len of a list or tuple is its number of elements. For class instances, define the __len__ method and code the length calculation yourself, then call len(instance) and Python will call your __len__ special method for you.
4 __delitem__ is called when you call del instance[key], which you may remember as the way to delete individual items from a dictionary. When you use del on a class instance, Python calls the __delitem__ special method for you.
Замечание
In Java, you determine whether two string variables reference the same physical memory location by using str1 == str2. This is called object identity, and it is written in Python as str1 is str2. To compare string values in Java, you would use str1.equals(str2); in Python, you would use str1 == str2. Java programmers who have been taught to believe that the world is a better place because == in Java compares by identity instead of by value may have a difficult time adjusting to Python's lack of such “gotchas”.

At this point, you may be thinking, “all this work just to do something in a class that I can do with a built-in datatype”. And it's true that life would be easier (and the entire UserDict class would be unnecessary) if you could inherit from built-in datatypes like a dictionary. But even if you could, special methods would still be useful, because they can be used in any class, not just wrapper classes like UserDict.

Special methods mean that any class can store key-value pairs like a dictionary, just by defining the __setitem__ method. Any class can act like a sequence, just by defining the __getitem__ method. Any class that defines the __cmp__ method can be compared with ==. And if your class represents something that has a length, don't define a GetLength method; define the __len__ method and use len(instance).

Замечание
While other object-oriented languages only let you define the physical model of an object (“this object has a GetLength method”), Python's special class methods like __len__ allow you to define the logical model of an object (“this object has a length”).

There are lots of other special methods. There's a whole set of them that let classes act like numbers, allowing you to add, subtract, and do other arithmetic operations on class instances. (The canonical example of this is a class that represents complex numbers, numbers with both real and imaginary components.) The __call__ method lets a class act like a function, allowing you to call a class instance directly. And there are other special methods that allow classes to have read-only and write-only data attributes; we'll talk more about those in later chapters.

Further reading


3.8. Class attributes

You already know about data attributes, which are variables owned by a specific instance of a class. Python also supports class attributes, which are variables owned by the class itself.

Пример 3.18. Introducing class attributes


class MP3FileInfo(FileInfo):
    "хранит ID3v1.0 MP3 теги"
    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)}
>>> import fileinfo
>>> fileinfo.MP3FileInfo            1
<class fileinfo.MP3FileInfo at 01257FDC>
>>> fileinfo.MP3FileInfo.tagDataMap 2
{'title': (3, 33, <function stripnulls at 0260C8D4>), 
'genre': (127, 128, <built-in function ord>), 
'artist': (33, 63, <function stripnulls at 0260C8D4>), 
'year': (93, 97, <function stripnulls at 0260C8D4>), 
'comment': (97, 126, <function stripnulls at 0260C8D4>), 
'album': (63, 93, <function stripnulls at 0260C8D4>)}
>>> m = fileinfo.MP3FileInfo()      3
>>> m.tagDataMap
{'title': (3, 33, <function stripnulls at 0260C8D4>), 
'genre': (127, 128, <built-in function ord>), 
'artist': (33, 63, <function stripnulls at 0260C8D4>), 
'year': (93, 97, <function stripnulls at 0260C8D4>), 
'comment': (97, 126, <function stripnulls at 0260C8D4>), 
'album': (63, 93, <function stripnulls at 0260C8D4>)}
1 MP3FileInfo is the class itself, not any particular instance of the class.
2 tagDataMap is a class attribute: literally, an attribute of the class. It is available before creating any instances of the class.
3 Class attributes are available both through direct reference to the class and through any instance of the class.
Замечание
In Java, both static variables (called class attributes in Python) and instance variables (called data attributes in Python) are defined immediately after the class definition (one with the static keyword, one without). In Python, only class attributes can be defined here; data attributes are defined in the __init__ method.

Class attributes can be used as class-level constants (which is how we use them in MP3FileInfo), but they are not really constants.[4] You can also change them.

Пример 3.19. Modifying class attributes

>>> class counter:
...     count = 0                     1
...     def __init__(self)
...         self.__class__.count += 1 2
...     
>>> counter
<class __main__.counter at 010EAECC>
>>> counter.count                     3
0
>>> c = counter()
>>> c.count                           4
1
>>> counter.count
1
>>> d = counter()                     5
>>> d.count
2
>>> c.count
2
>>> counter.count
2
1 count is a class attribute of the counter class.
2 __class__ is a built-in attribute of every class instance (of every class). It is a reference to the class that self is an instance of (in this case, the counter class).
3 Because count is a class attribute, it is available through direct reference to the class, before we have created any instances of the class.
4 Creating an instance of the class calls the __init__ method, which increments the class attribute count by 1. This affects the class itself, not just the newly created instance.
5 Creating a second instance will increment the class attribute count again. Notice how the class attribute is shared by the class and all instances of the class.

Footnotes

[4] There are no constants in Python. Everything can be changed if you try hard enough. This fits with one of the core principles of Python: bad behavior should be discouraged but not banned. If you really want to change the value of None, you can do it, but don't come running to me when your code is impossible to debug.


3.9. Private functions

Like most languages, Python has the concept of private functions, which can not be called from outside their module; private class methods, which can not be called from outside their class; and private attributes, which can not be accessed from outside their class. Unlike most languages, whether a Python function, method, or attribute is private or public is determined entirely by its name.

In MP3FileInfo, there are two methods: __parse and __setitem__. As we have already discussed, __setitem__ is a special method; normally, you would call it indirectly by using the dictionary syntax on a class instance, but it is public, and you could call it directly (even from outside the fileinfo module) if you had a really good reason. However, __parse is private, because it has two underscores at the beginning of its name.

Замечание
If the name of a Python function, class method, or attribute starts with (but doesn't end with) two underscores, it's private; everything else is public.
Замечание
In Python, all special methods (like __setitem__) and built-in attributes (like __doc__) follow a standard naming convention: they both start with and end with two underscores. Don't name your own methods and attributes this way; it will only confuse you (and others) later.
Замечание
Python has no concept of protected class methods (accessible only in their own class and descendant classes). Class methods are either private (accessible only in their own class) or public (accessible from anywhere).

Пример 3.20. Trying to call a private method

>>> import fileinfo
>>> m = fileinfo.MP3FileInfo()
>>> m.__parse("/music/_singles/kairo.mp3") 1
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
AttributeError: 'MP3FileInfo' instance has no attribute '__parse'
1 If you try to call a private method, Python will raise a slightly misleading exception, saying that the method does not exist. Of course it does exist, but it's private, so it's not accessible outside the class.[5]

Further reading

Footnotes

[5] Strictly speaking, private methods are accessible outside their class, just not easily accessible. Nothing in Python is truly private; internally, the names of private methods and attributes are mangled and unmangled on the fly to make them seem inaccessible by their given names. You can access the __parse method of the MP3FileInfo class by the name _MP3FileInfo__parse. Acknowledge that this is interesting, then promise to never, ever do it in real code. Private methods are private for a reason, but like many other things in Python, their privateness is ultimately a matter of convention, not force.


3.10. Handling exceptions

Like many object-oriented languages, Python has exception handling via try...except blocks.

Замечание
Python uses try...except to handle exceptions and raise to generate them. Java and C++ use try...catch to handle exceptions, and throw to generate them.

If you already know all about exceptions, you can skim this section. If you've been stuck programming in a lesser language that doesn't have exception handling, or you've been using a real language but not using exceptions, this section is very important.

Exceptions are everywhere in Python; virtually every module in the standard Python library uses them, and Python itself will raise them in lots of different circumstances. You've already seen them repeatedly throughout this book.

In each of these cases, we were simply playing around in the Python IDE: an error occurred, the exception was printed (depending on your IDE, in an intentionally jarring shade of red), and that was that. This is called an unhandled exception; when the exception was raised, there was no code to explicitly notice it and deal with it, so it bubbled its way back to the default behavior built in to Python, which is to spit out some debugging information and give up. In the IDE, that's no big deal, but if that happened while your actual Python program was running, the entire program would come to a screeching halt.[6]

An exception doesn't have to be a complete program crash, though. Exceptions, when raised, can be handled. Sometimes an exception is really because you have a bug in your code (like accessing a variable that doesn't exist), but many times, an exception is something you can plan for. If you're opening a file, it might not exist; if you're connecting to a database, it might be unavailable, or you might not have the correct security credentials to access it. If you know a line of code may raise an exception, you should handle the exception using a try...except block.

Пример 3.21. Opening a non-existent file

>>> fsock = open("/notthere", "r")      1
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
IOError: [Errno 2] No such file or directory: '/notthere'
>>> try:
...     fsock = open("/notthere")       2
... except IOError:                     3
...     print "The file does not exist, exiting gracefully"
... print "This line will always print" 4
The file does not exist, exiting gracefully
This line will always print
1 Using the built-in open function, we can try to open a file for reading (more on open in the next section). But the file doesn't exist, so this raises the IOError exception. Since we haven't provided any explicit check for an IOError exception, Python just prints out some debugging information about what happened and then gives up.
2 We're trying to open the same non-existent file, but this time we're doing it within a try...except block.
3 When the open method raises an IOError exception, we're ready for it. The except IOError: line catches the exception and executes our own block of code, which in this case just prints a more pleasant error message.
4 Once an exception has been handled, processing continues normally on the first line after the try...except block. Note that this line will always print, whether or not an exception occurs. If you really did have a file called notthere in your root directory, the call to open would succeed, the except clause would be ignored, and this line would still be executed.

Exceptions may seem unfriendly (after all, if you don't catch the exception, your entire program will crash), but consider the alternative. Would you rather get back an unusable file object to a non-existent file? You'd have to check its validity somehow anyway, and if you forgot, your program would give you strange errors somewhere down the line that you would have to trace back to the source. I'm sure you've done this; it's not fun. With exceptions, errors occur immediately, and you can handle them in a standard way at the source of the problem.

There are lots of other uses for exceptions besides handling actual error conditions. A common use in the standard Python library is to try to import a module, then check whether it worked. Importing a module that does not exist will raise an ImportError exception. You can use this to define multiple levels of functionality based on which modules are available at run-time, or to support multiple platforms (where platform-specific code is separated into different modules).

Пример 3.22. Supporting platform-specific functionality

This code comes from the getpass module, a wrapper module for getting a password from the user. Getting a password is accomplished differently on UNIX, Windows, and Mac OS platforms, but this code encapsulates all of those differences.

  # Bind the name getpass to the appropriate function
  try:
      import termios, TERMIOS                     1
  except ImportError:
      try:
          import msvcrt                           2
      except ImportError:
          try:
              from EasyDialogs import AskPassword 3
          except ImportError:
              getpass = default_getpass           4
          else:                                   5
              getpass = AskPassword
      else:
          getpass = win_getpass
  else:
      getpass = unix_getpass
1 termios is a UNIX-specific module that provides low-level control over the input terminal. If this module is not available (because it's not on your system, or your system doesn't support it), the import fails and Python raises an ImportError, which we catch.
2 OK, we didn't have termios, so let's try msvcrt, which is a Windows-specific module that provides an API to lots of useful functions in the Microsoft Visual C++ runtime services. If this import fails, Python will raise an ImportError, which we catch.
3 If the first two didn't work, we try to import a function from EasyDialogs, which is a Mac OS-specific module that provides functions to pop up dialogs of various types. Once again, if this import fails, Python will raise an ImportError, which we catch.
4 None of these platform-specific modules is available (which is possible, since Python has been ported to lots of different platforms), so we have to fall back on a default password input function (which is defined elsewhere in the getpass module). Notice what we're doing here: we're assigning the function default_getpass to the variable getpass. If you read the official getpass documentation, it tells you that the getpass module defines a getpass function. This is how it does it: by binding getpass to the right function for your platform. Then when you call the getpass function, you're really calling a platform-specific function that this code has set up for you. You don't have to know or care what platform your code is running on; just call getpass, and it will always do the right thing.
5 A try...except block can have an else clause, like an if statement. If no exception is raised during the try block, the else clause is executed afterwards. In this case, that means that the from EasyDialogs import AskPassword import worked, so we should bind getpass to the AskPassword function. Each of the other try...except blocks have similar else clauses to bind getpass to the appropriate function when we find an import that works.

Further reading

Footnotes

[6] Or, as some marketroids would put it, your program would perform an illegal action. Whatever.


3.11. File objects

Python has a built-in function, open, for opening a file on disk. open returns a file object, which has methods and attributes for getting information about and manipulating the opened file.

Пример 3.23. Opening a file

>>> f = open("/music/_singles/kairo.mp3", "rb") 1
>>> f                                           2
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.mode                                      3
'rb'
>>> f.name                                      4
'/music/_singles/kairo.mp3'
1 The open method can take up to three parameters: a filename, a mode, and a buffering parameter. Only the first one, the filename, is required; the other two are optional. If not specified, the file is opened for reading in text mode. Here we are opening the file for reading in binary mode. (print open.__doc__ displays a great explanation of all the possible modes.)
2 The open function returns an object (by now, this should not surprise you). A file object has several useful attributes.
3 The mode attribute of a file object tells you what mode the file was opened in.
4 The name attribute of a file object tells you the name of the file that the file object has open.

Пример 3.24. Reading a file

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.tell()              1
0
>>> f.seek(-128, 2)       2
>>> f.tell()              3
7542909
>>> tagData = f.read(128) 4
>>> tagData
'TAGKAIRO****THE BEST GOA         ***DJ MARY-JANE***            Rave Mix                      2000http://mp3.com/DJMARYJANE     \037'
>>> f.tell()              5
7543037
1 A file object maintains state about the file it has open. The tell method of a file object tells you your current position in the open file. Since we haven't done anything with this file yet, the current position is 0, which is the beginning of the file.
2 The seek method of a file object moves to another position in the open file. The second parameter specifies what the first one means; 0 means move to an absolute position (counting from the start of the file), 1 means move to a relative position (counting from the current position), and 2 means move to a position relative to the end of the file. Since the MP3 tags we're looking for are stored at the end of the file, we use 2 and tell the file object to move to a position 128 bytes from the end of the file.
3 The tell method confirms that the current file position has moved.
4 The read method reads a specified number of bytes from the open file and returns a string with the data which was read. The optional parameter specifies the maximum number of bytes to read. If no parameter is specified, read will read until the end of the file. (We could have simply said read() here, since we know exactly where we are in the file and we are, in fact, reading the last 128 bytes.) The read data is assigned to the tagData variable, and the current position is updated based on how many bytes were read.
5 The tell method confirms that the current position has moved. If you do the math, you'll see that after reading 128 bytes, the position has been incremented by 128.

Пример 3.25. Closing a file

>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed  1
0
>>> f.close() 2
>>> f
<closed file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988>
>>> f.closed
1
>>> f.seek(0) 3
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.tell()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.read()
Traceback (innermost last):
  File "<interactive input>", line 1, in ?
ValueError: I/O operation on closed file
>>> f.close() 4
1 The closed attribute of a file object indicates whether the object has a file open or not. In this case, the file is still open (closed is 0). Open files consume system resources, and depending on the file mode, other programs may not be able to access them. It's important to close files as soon as you're done with them.
2 To close a file, call the close method of the file object. This frees the lock (if any) that you were holding on the file, flushes buffered writes (if any) that the system hadn't gotten around to actually writing yet, and releases the system resources. The closed attribute confirms that the file is closed.
3 Just because a file is closed doesn't mean that the file object ceases to exist. The variable f will continue to exist until it goes out of scope or gets manually deleted. However, none of the methods that manipulate an open file will work once the file has been closed; they all raise an exception.
4 Calling close on a file object whose file is already closed does not raise an exception; it fails silently.

Пример 3.26. File objects in MP3FileInfo

        try:                                1
            fsock = open(filename, "rb", 0) 2
            try:                           
                fsock.seek(-128, 2)         3
                tagdata = fsock.read(128)   4
            finally:                        5
                fsock.close()              
            .
            .
            .
        except IOError:                     6
            pass                           
1 Because opening and reading files is risky and may raise an exception, all of this code is wrapped in a try...except block. (Hey, isn't standardized indentation great? This is where you start to appreciate it.)
2 The open function may raise an IOError. (Maybe the file doesn't exist.)
3 The seek method may raise an IOError. (Maybe the file is smaller than 128 bytes.)
4 The read method may raise an IOError. (Maybe the disk has a bad sector, or it's on a network drive and the network just went down.)
5 This is new: a try...finally block. Once the file has been opened successfully by the open function, we want to make absolutely sure that we close it, even if an exception is raised by the seek or read methods. That's what a try...finally block is for: code in the finally block will always be executed, even if something in the try block raises an exception. Think of it as code that gets executed “on the way out”, regardless of what happened on the way.
6 At last, we handle our IOError exception. This could be the IOError exception raised by the call to open, seek, or read. Here, we really don't care, because all we're going to do is ignore it silently and continue. (Remember, pass is a Python statement that does nothing.) That's perfectly legal; “handling” an exception can mean explicitly doing nothing. It still counts as handled, and processing will continue normally on the next line of code after the try...except block.

Further reading


3.12. for loops

Like most other languages, Python has for loops. The only reason you haven't seen them until now is that Python is good at so many other things that you don't need them as often.

Most other languages don't have a powerful list datatype like Python, so you end up doing a lot of manual work, specifying a start, end, and step to define a range of integers or characters or other iteratable entities. But in Python, a for loop simply iterates over a list, the same way list comprehensions work.

Пример 3.27. Introducing the for loop

>>> li = ['a', 'b', 'e']
>>> for s in li:         1
...     print s          2
a
b
e
>>> print "\n".join(li)  3
a
b
e
1 The syntax for a for loop is similar to list comprehensions. li is a list, and s will take the value of each element in turn, starting from the first element.
2 Like an if statement or any other indented block, a for loop can have any number of lines of code in it.
3 This is the reason you haven't seen the for loop yet: we haven't needed it yet. It's amazing how often you use for loops in other languages when all you really want is a join or a list comprehension.

Пример 3.28. Simple counters

>>> for i in range(5):       1
...     print i
0
1
2
3
4
>>> li = ['a', 'b', 'c', 'd', 'e']
>>> for i in range(len(li)): 2
...     print li[i]
a
b
c
d
e
1 Doing a “normal” (by Visual Basic standards) counter for loop is also simple. As we saw in Пример 1.27. Присваивание идущих подряд значений, range produces a list of integers, which we then loop through. I know it looks a bit odd, but it is occasionally (and I stress occasionally) useful to have a counter loop.
2 Don't ever do this. This is Visual Basic-style thinking. Break out of it. Just iterate through the list, as shown in the previous example.

Пример 3.29. Iterating through a dictionary

>>> for k, v in os.environ.items(): 1 2
...     print "%s=%s" % (k, v)
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
>>> print "\n".join(["%s=%s" % (k, v) for k, v in os.environ.items()]) 3
USERPROFILE=C:\Documents and Settings\mpilgrim
OS=Windows_NT
COMPUTERNAME=MPILGRIM
USERNAME=mpilgrim

[...snip...]
1 os.environ is a dictionary of the environment variables defined on your system. In Windows, these are your user and system variables accessible from MS-DOS. In UNIX, they are the variables exported in your shell's startup scripts. In Mac OS, there is no concept of environment variables, so this dictionary is empty.
2 os.environ.items() returns a list of tuples: [(key1, value1), (key2, value2), ...]. The for loop iterates through this list. The first round, it assigns key1 to k and value1 to v, so k = USERPROFILE and v = C:\Documents and Settings\mpilgrim. The second round, k gets the second key, OS, and v gets the corresponding value, Windows_NT.
3 With multi-variable assignment and list comprehensions, you can replace the entire for loop with a single statement. Whether you actually do this in real code is a matter of personal coding style; I like it because it makes it clear that what we're doing is mapping a dictionary into a list, then joining the list into a single string. Other programmers prefer to write this out as a for loop. Note that the output is the same in either case, although this version is slightly faster, because there is only one print statement instead of many.

Пример 3.30. for loop in MP3FileInfo

    tagDataMap = {"title"   : (  3,  33, stripnulls),
                  "artist"  : ( 33,  63, stripnulls),
                  "album"   : ( 63,  93, stripnulls),
                  "year"    : ( 93,  97, stripnulls),
                  "comment" : ( 97, 126, stripnulls),
                  "genre"   : (127, 128, ord)} 1
    .
    .
    .
            if tagdata[:3] == "TAG":
                for tag, (start, end, parseFunc) in self.tagDataMap.items(): 2
                    self[tag] = parseFunc(tagdata[start:end])                3
1 tagDataMap is a class attribute that defines the tags we're looking for in an MP3 file. Tags are stored in fixed-length fields; once we read the last 128 bytes of the file, bytes 3 through 32 of those are always the song title, 33-62 are always the artist name, 63-92 the album name, and so forth. Note that tagDataMap is a dictionary of tuples, and each tuple contains two integers and a function reference.
2 This looks complicated, but it's not. The structure of the for variables matches the structure of the elements of the list returned by items. Remember, items returns a list of tuples of the form (key, value). The first element of that list is ("title", (3, 33, <function stripnulls>)), so the first time around the loop, tag gets "title", start gets 3, end gets 33, and parseFunc gets the function stripnulls.
3 Now that we've extracted all the parameters for a single MP3 tag, saving the tag data is easy. We slice tagdata from start to end to get the actual data for this tag, call parseFunc to post-process the data, and assign this as the value for the key tag in the pseudo-dictionary self. After iterating through all the elements in tagDataMap, self has the values for all the tags, and you know what that looks like.

3.13. More on modules

Modules, like everything else in Python, are objects. Once imported, you can always get a reference to a module through the global dictionary sys.modules.

Пример 3.31. Introducing sys.modules

>>> import sys                          1
>>> print '\n'.join(sys.modules.keys()) 2
win32api
os.path
os
exceptions
__main__
ntpath
nt
sys
__builtin__
site
signal
UserDict
stat
1 The sys module contains system-level information, like the version of Python you're running (sys.version or sys.version_info), and system-level options like the maximum allowed recursion depth (sys.getrecursionlimit() and sys.setrecursionlimit()).
2 sys.modules is a dictionary containing all the modules that have ever been imported since Python was started; the key is the module name, the value is the module object. Note that this is more than just the modules your program has imported. Python preloads some modules on startup, and if you're in a Python IDE, sys.modules contains all the modules imported by all the programs you've run within the IDE.

Пример 3.32. Using sys.modules

>>> import fileinfo         1
>>> print '\n'.join(sys.modules.keys())
win32api
os.path
os
fileinfo
exceptions
__main__
ntpath
nt
sys
__builtin__
site
signal
UserDict
stat
>>> fileinfo
<module 'fileinfo' from 'fileinfo.pyc'>
>>> sys.modules["fileinfo"] 2
<module 'fileinfo' from 'fileinfo.pyc'>
1 As new modules are imported, they are added to sys.modules. This explains why importing the same module twice is very fast: Python has already loaded and cached the module in sys.modules, so importing the second time is simply a dictionary lookup.
2 Given the name (as a string) of any previously-imported module, you can get a reference to the module itself through the sys.modules dictionary.

Пример 3.33. The __module__ class attribute

>>> from fileinfo import MP3FileInfo
>>> MP3FileInfo.__module__              1
'fileinfo'
>>> sys.modules[MP3FileInfo.__module__] 2
<module 'fileinfo' from 'fileinfo.pyc'>
1 Every Python class has a built-in class attribute __module__, which is the name of the module in which the class is defined.
2 Combining this with the sys.modules dictionary, you can get a reference to the module in which a class is defined.

Пример 3.34. sys.modules in fileinfo.py

    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):       1
        "оределяет класс, предназначеный для обработки файла, по расширению"      
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]        2
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo 3
1 This is a function with two arguments; filename is required, but module is optional and defaults to the module which contains the FileInfo class. This looks inefficient, because you might expect Python to evaluate the sys.modules expression every time the function is called. In fact, Python only evaluates default expressions once, the first time the module is imported. As we'll see later, we never call this function with a module argument, so module serves as a function-level constant.
2 We'll plough through this line later, after we dive into the os module. For now, take it on faith that subclass ends up as the name of a class, like MP3FileInfo.
3 You already know about getattr, which gets a reference to an object by name. hasattr is a complementary function that checks whether an object has a particular attribute; in this case, whether a module has a particular class (although it works for any object and any attribute, just like getattr). In English, this line of code says “if this module has the class named by subclass then return it, otherwise return the base class FileInfo”.

Further reading


3.14. The os module

The os module has lots of useful functions for manipulating files and processes, and os.path has functions for manipulating file and directory paths.

Пример 3.35. Constructing pathnames

>>> import os
>>> os.path.join("c:\\music\\ap\\", "mahadeva.mp3") 1 2
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.join("c:\\music\\ap", "mahadeva.mp3")   3
'c:\\music\\ap\\mahadeva.mp3'
>>> os.path.expanduser("~")                         4
'c:\\Documents and Settings\\mpilgrim\\My Documents'
>>> os.path.join(os.path.expanduser("~"), "Python") 5
'c:\\Documents and Settings\\mpilgrim\\My Documents\\Python'
1 os.path is a reference to a module; which module it is depends on what platform you're running on. Just like getpass encapsulates differences between platforms by setting getpass to a platform-specific function, os encapsulates differences between platforms by setting path to a platform-specific module.
2 The join function of os.path constructs a pathname out of one or more partial pathnames. In this simple case, it simply concatenates strings. (Note that dealing with pathnames on Windows is annoying because the backslash character must be escaped.)
3 In this slightly less trivial case, join will add an extra backslash to the pathname before joining it to the filename. I was overjoyed when I discovered this, since addSlashIfNecessary is always one of the stupid little functions I have to write when building up my toolbox in a new language. Do not write this stupid little function in Python; smart people have already taken care of it for you.
4 expanduser will expand a pathname that uses ~ to represent the current user's home directory. This works on any platform where users have a home directory, like Windows, UNIX, and Mac OS X; it has no effect on Mac OS.
5 Combining these techniques, you can easily construct pathnames for directories and files under the user's home directory.

Пример 3.36. Splitting pathnames

>>> os.path.split("c:\\music\\ap\\mahadeva.mp3")                        1
('c:\\music\\ap', 'mahadeva.mp3')
>>> (filepath, filename) = os.path.split("c:\\music\\ap\\mahadeva.mp3") 2
>>> filepath                                                            3
'c:\\music\\ap'
>>> filename                                                            4
'mahadeva.mp3'
>>> (shortname, extension) = os.path.splitext(filename)                 5
>>> shortname
'mahadeva'
>>> extension
'.mp3'
1 The split function splits a full pathname and returns a tuple containing the path and filename. Remember when I said you could use multi-variable assignment to return multiple values from a function? Well, split is such a function.
2 We assign the return value of the split function into a tuple of two variables. Each variable receives the value of the corresponding element of the returned tuple.
3 The first variable, filepath, receives the value of the first element of the tuple returned from split, the file path.
4 The second variable, filename, receives the value of the second element of the tuple returned from split, the filename.
5 os.path also contains a function splitext, which splits a filename and returns a tuple containing the filename and the file extension. We use the same technique to assign each of them to separate variables.

Пример 3.37. Listing directories

>>> os.listdir("c:\\music\\_singles\\")                                          1
['a_time_long_forgotten_con.mp3', 'hellraiser.mp3', 'kairo.mp3',
'long_way_home1.mp3', 'sidewinder.mp3', 'spinning.mp3']
>>> dirname = "c:\\"
>>> os.listdir(dirname)                                                          2
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'cygwin', 'docbook',
'Documents and Settings', 'Incoming', 'Inetpub', 'IO.SYS', 'MSDOS.SYS', 'Music',
'NTDETECT.COM', 'ntldr', 'pagefile.sys', 'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
>>> [f for f in os.listdir(dirname) if os.path.isfile(os.path.join(dirname, f))] 3
['AUTOEXEC.BAT', 'boot.ini', 'CONFIG.SYS', 'IO.SYS', 'MSDOS.SYS',
'NTDETECT.COM', 'ntldr', 'pagefile.sys']
>>> [f for f in os.listdir(dirname) if os.path.isdir(os.path.join(dirname, f))]  4
['cygwin', 'docbook', 'Documents and Settings', 'Incoming',
'Inetpub', 'Music', 'Program Files', 'Python20', 'RECYCLER',
'System Volume Information', 'TEMP', 'WINNT']
1 The listdir function takes a pathname and returns a list of the contents of the directory.
2 listdir returns both files and folders, with no indication of which is which.
3 You can use list filtering and the isfile function of the os.path module to separate the files from the folders. isfile takes a pathname and returns 1 if the path represents a file, and 0 otherwise. Here we're using os.path.join to ensure a full pathname, but isfile also works with a partial path, relative to the current working directory. You can use os.path.getcwd() to get the current working directory.
4 os.path also has a isdir function which returns 1 if the path represents a directory, and 0 otherwise. You can use this to get a list of the subdirectories within a directory.

Пример 3.38. Listing directories in fileinfo.py


def listDirectory(directory, fileExtList):                                        
    """возвращает список объектов с метаинформацией для всех файлов с
указанным расширением"""                       
    fileList = [os.path.normcase(f) for f in os.listdir(directory)]               
    fileList = [os.path.join(directory, f) for f in fileList \
                if os.path.splitext(f)[1] in fileExtList]                         

These two lines of code combine everything we've learned so far about the os module, and then some.

  1. os.listdir(directory) returns a list of all the files and folders in directory.
  2. Iterating through the list with f, we use os.path.normcase(f) to normalize the case according to operating system defaults. normcase is a useful little function that compensates for case-insensitive operating systems that think that mahadeva.mp3 and mahadeva.MP3 are the same file. For instance, on Windows and Mac OS, normcase will convert the entire filename to lowercase; on UNIX-compatible systems, it will return the filename unchanged.
  3. Iterating through the normalized list with f again, we use os.path.splitext(f) to split each filename into name and extension.
  4. For each file, we see if the extension is in the list of file extensions we care about (fileExtList, which was passed to the listDirectory function).
  5. For each file we care about, we use os.path.join(directory, f) to construct the full pathname of the file, and return a list of the full pathnames.
Замечание
Whenever possible, you should use the functions in os and os.path for file, directory, and path manipulations. These modules are wrappers for platform-specific modules, so functions like os.path.split work on UNIX, Windows, Mac OS, and any other supported Python platform.

Further reading


3.15. Putting it all together

Once again, all the dominoes are in place. We've seen how each line of code works. Now let's step back and see how it all fits together.

Пример 3.39. listDirectory


def listDirectory(directory, fileExtList):                                         1
    """возвращает список объектов с метаинформацией для всех файлов с
указанным расширением"""                       
    fileList = [os.path.normcase(f) for f in os.listdir(directory)]               
    fileList = [os.path.join(directory, f) for f in fileList \
                if os.path.splitext(f)[1] in fileExtList]                          2
    def getFileInfoClass(filename, module=sys.modules[FileInfo.__module__]):       3
        "оределяет класс, предназначеный для обработки файла, по расширению"      
        subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]        4
        return hasattr(module, subclass) and getattr(module, subclass) or FileInfo 5
    return [getFileInfoClass(f)(f) for f in fileList]                              6
1 listDirectory is the main attraction of this entire module. It takes a directory (like c:\music\_singles\ in my case) and a list of interesting file extensions (like ['.mp3']), and it returns a list of class instances that act like dictionaries that contain metadata about each interesting file in that directory. And it does it in just a few straightforward lines of code.
2 As we saw in the previous section, this line of code gets a list of the full pathnames of all the files in directory that have an interesting file extension (as specified by fileExtList).
3 Old-school Pascal programmers may be familiar with them, but most people give me a blank stare when I tell them that Python supports nested functions -- literally, a function within a function. The nested function getFileInfoClass can only be called from the function in which it is defined, listDirectory. As with any other function, you don't need an interface declaration or anything fancy; just define the function and code it.
4 Now that you've seen the os module, this line should make more sense. It gets the extension of the file (os.path.splitext(filename)[1]), forces it to uppercase (.upper()), slices off the dot ([1:]), and constructs a class name out of it with string formatting. So c:\music\ap\mahadeva.mp3 becomes .mp3 becomes .MP3 becomes MP3 becomes MP3FileInfo.
5 Having constructed the name of the handler class that would handle this file, we check to see if that handler class actually exists in this module. If it does, we return the class, otherwise we return the base class FileInfo. This is a very important point: this function returns a class. Not an instance of a class, but the class itself.
6 For each file in our “interesting files” list (fileList), we call getFileInfoClass with the filename (f). Calling getFileInfoClass(f) returns a class; we don't know exactly which class, but we don't care. We then create an instance of this class (whatever it is) and pass the filename (f again), to the __init__ method. As we saw earlier in this chapter, the __init__ method of FileInfo sets self["name"], which triggers __setitem__, which is overridden in the descendant (MP3FileInfo) to parse the file appropriately to pull out the file's metadata. We do all that for each interesting file and return a list of the resulting instances.

Note that listDirectory is completely generic. It doesn't know ahead of time which types of files it will be getting, or which classes are defined that could potentially handle those files. It inspects the directory for the files to process, then introspects its own module to see what special handler classes (like MP3FileInfo) are defined. You can extend this program to handle other types of files simply by defining an appropriately-named class: HTMLFileInfo for HTML files, DOCFileInfo for Word .doc files, and so forth. listDirectory will handle them all, without modification, by handing the real work off to the appropriate classes and collating the results.


A.1. Applicability and definitions

This License applies to any manual or other work that contains a notice placed by the copyright holder saying it can be distributed under the terms of this License. The "Document", below, refers to any such manual or work. Any member of the public is a licensee, and is addressed as "you".

A "Modified Version" of the Document means any work containing the Document or a portion of it, either copied verbatim, or with modifications and/or translated into another language.

A "Secondary Section" is a named appendix or a front-matter section of the Document that deals exclusively with the relationship of the publishers or authors of the Document to the Document's overall subject (or to related matters) and contains nothing that could fall directly within that overall subject. (For example, if the Document is in part a textbook of mathematics, a Secondary Section may not explain any mathematics.) The relationship could be a matter of historical connection with the subject or with related matters, or of legal, commercial, philosophical, ethical or political position regarding them.

The "Invariant Sections" are certain Secondary Sections whose titles are designated, as being those of Invariant Sections, in the notice that says that the Document is released under this License.

The "Cover Texts" are certain short passages of text that are listed, as Front-Cover Texts or Back-Cover Texts, in the notice that says that the Document is released under this License.

A "Transparent" copy of the Document means a machine-readable copy, represented in a format whose specification is available to the general public, whose contents can be viewed and edited directly and straightforwardly with generic text editors or (for images composed of pixels) generic paint programs or (for drawings) some widely available drawing editor, and that is suitable for input to text formatters or for automatic translation to a variety of formats suitable for input to text formatters. A copy made in an otherwise Transparent file format whose markup has been designed to thwart or discourage subsequent modification by readers is not Transparent. A copy that is not "Transparent" is called "Opaque".

Examples of suitable formats for Transparent copies include plain ASCII without markup, Texinfo input format, LaTeX input format, SGML or XML using a publicly available DTD, and standard-conforming simple HTML designed for human modification. Opaque formats include PostScript, PDF, proprietary formats that can be read and edited only by proprietary word processors, SGML or XML for which the DTD and/or processing tools are not generally available, and the machine-generated HTML produced by some word processors for output purposes only.

The "Title Page" means, for a printed book, the title page itself, plus such following pages as are needed to hold, legibly, the material this License requires to appear in the title page. For works in formats which do not have any title page as such, "Title Page" means the text near the most prominent appearance of the work's title, preceding the beginning of the body of the text.


A.2. Verbatim copying

You may copy and distribute the Document in any medium, either commercially or noncommercially, provided that this License, the copyright notices, and the license notice saying this License applies to the Document are reproduced in all copies, and that you add no other conditions whatsoever to those of this License. You may not use technical measures to obstruct or control the reading or further copying of the copies you make or distribute. However, you may accept compensation in exchange for copies. If you distribute a large enough number of copies you must also follow the conditions in section 3.

You may also lend copies, under the same conditions stated above, and you may publicly display copies.


A.3. Copying in quantity

If you publish printed copies of the Document numbering more than 100, and the Document's license notice requires Cover Texts, you must enclose the copies in covers that carry, clearly and legibly, all these Cover Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on the back cover. Both covers must also clearly and legibly identify you as the publisher of these copies. The front cover must present the full title with all words of the title equally prominent and visible. You may add other material on the covers in addition. Copying with changes limited to the covers, as long as they preserve the title of the Document and satisfy these conditions, can be treated as verbatim copying in other respects.

If the required texts for either cover are too voluminous to fit legibly, you should put the first ones listed (as many as fit reasonably) on the actual cover, and continue the rest onto adjacent pages.

If you publish or distribute Opaque copies of the Document numbering more than 100, you must either include a machine-readable Transparent copy along with each Opaque copy, or state in or with each Opaque copy a publicly-accessible computer-network location containing a complete Transparent copy of the Document, free of added material, which the general network-using public has access to download anonymously at no charge using public-standard network protocols. If you use the latter option, you must take reasonably prudent steps, when you begin distribution of Opaque copies in quantity, to ensure that this Transparent copy will remain thus accessible at the stated location until at least one year after the last time you distribute an Opaque copy (directly or through your agents or retailers) of that edition to the public.

It is requested, but not required, that you contact the authors of the Document well before redistributing any large number of copies, to give them a chance to provide you with an updated version of the Document.


A.4. Modifications

You may copy and distribute a Modified Version of the Document under the conditions of sections 2 and 3 above, provided that you release the Modified Version under precisely this License, with the Modified Version filling the role of the Document, thus licensing distribution and modification of the Modified Version to whoever possesses a copy of it. In addition, you must do these things in the Modified Version:

  1. Use in the Title Page (and on the covers, if any) a title distinct from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.
  2. List on the Title Page, as authors, one or more persons or entities responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).
  3. State on the Title page the name of the publisher of the Modified Version, as the publisher.
  4. Preserve all the copyright notices of the Document.
  5. Add an appropriate copyright notice for your modifications adjacent to the other copyright notices.
  6. Include, immediately after the copyright notices, a license notice giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.
  7. Preserve in that license notice the full lists of Invariant Sections and required Cover Texts given in the Document's license notice.
  8. Include an unaltered copy of this License.
  9. Preserve the section entitled "History", and its title, and add to it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.
  10. Preserve the network location, if any, given in the Document for public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.
  11. In any section entitled "Acknowledgements" or "Dedications", preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.
  12. Preserve all the Invariant Sections of the Document, unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.
  13. Delete any section entitled "Endorsements". Such a section may not be included in the Modified Version.
  14. Do not retitle any existing section as "Endorsements" or to conflict in title with any Invariant Section.

If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.

You may add a section entitled "Endorsements", provided it contains nothing but endorsements of your Modified Version by various parties--for example, statements of peer review or that the text has been approved by an organization as the authoritative definition of a standard.

You may add a passage of up to five words as a Front-Cover Text, and a passage of up to 25 words as a Back-Cover Text, to the end of the list of Cover Texts in the Modified Version. Only one passage of Front-Cover Text and one of Back-Cover Text may be added by (or through arrangements made by) any one entity. If the Document already includes a cover text for the same cover, previously added by you or by arrangement made by the same entity you are acting on behalf of, you may not add another; but you may replace the old one, on explicit permission from the previous publisher that added the old one.

The author(s) and publisher(s) of the Document do not by this License give permission to use their names for publicity for or to assert or imply endorsement of any Modified Version.


A.5. Combining documents

You may combine the Document with other documents released under this License, under the terms defined in section 4 above for modified versions, provided that you include in the combination all of the Invariant Sections of all of the original documents, unmodified, and list them all as Invariant Sections of your combined work in its license notice.

The combined work need only contain one copy of this License, and multiple identical Invariant Sections may be replaced with a single copy. If there are multiple Invariant Sections with the same name but different contents, make the title of each such section unique by adding at the end of it, in parentheses, the name of the original author or publisher of that section if known, or else a unique number. Make the same adjustment to the section titles in the list of Invariant Sections in the license notice of the combined work.

In the combination, you must combine any sections entitled "History" in the various original documents, forming one section entitled "History"; likewise combine any sections entitled "Acknowledgements", and any sections entitled "Dedications". You must delete all sections entitled "Endorsements."


A.6. Collections of documents

You may make a collection consisting of the Document and other documents released under this License, and replace the individual copies of this License in the various documents with a single copy that is included in the collection, provided that you follow the rules of this License for verbatim copying of each of the documents in all other respects.

You may extract a single document from such a collection, and distribute it individually under this License, provided you insert a copy of this License into the extracted document, and follow this License in all other respects regarding verbatim copying of that document.


A.7. Aggregation with independent works

A compilation of the Document or its derivatives with other separate and independent documents or works, in or on a volume of a storage or distribution medium, does not as a whole count as a Modified Version of the Document, provided no compilation copyright is claimed for the compilation. Such a compilation is called an "aggregate", and this License does not apply to the other self-contained works thus compiled with the Document, on account of their being thus compiled, if they are not themselves derivative works of the Document.

If the Cover Text requirement of section 3 is applicable to these copies of the Document, then if the Document is less than one quarter of the entire aggregate, the Document's Cover Texts may be placed on covers that surround only the Document within the aggregate. Otherwise they must appear on covers around the whole aggregate.


A.8. Translation

Translation is considered a kind of modification, so you may distribute translations of the Document under the terms of section 4. Replacing Invariant Sections with translations requires special permission from their copyright holders, but you may include translations of some or all Invariant Sections in addition to the original versions of these Invariant Sections. You may include a translation of this License provided that you also include the original English version of this License. In case of a disagreement between the translation and the original English version of this License, the original English version will prevail.


A.9. Termination

You may not copy, modify, sublicense, or distribute the Document except as expressly provided for under this License. Any other attempt to copy, modify, sublicense or distribute the Document is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.


A.10. Future revisions of this license

The Free Software Foundation may publish new, revised versions of the GNU Free Documentation License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. See http://www.gnu.org/copyleft/.

Each version of the License is given a distinguishing version number. If the Document specifies that a particular numbered version of this License "or any later version" applies to it, you have the option of following the terms and conditions either of that specified version or of any later version that has been published (not as a draft) by the Free Software Foundation. If the Document does not specify a version number of this License, you may choose any version ever published (not as a draft) by the Free Software Foundation.


A.11. How to use this License for your documents

To use this License in a document you have written, include a copy of the License in the document and put the following copyright and license notices just after the title page:

Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".

If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.

If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software.


Приложение B. Python 2.1.1 license

Содержание

B.A. History of the software

Python was created in the early 1990s by Guido van Rossum at Stichting Mathematisch Centrum (CWI) in the Netherlands as a successor of a language called ABC. Guido is Python's principal author, although it includes many contributions from others. The last version released from CWI was Python 1.2. In 1995, Guido continued his work on Python at the Corporation for National Research Initiatives (CNRI) in Reston, Virginia where he released several versions of the software. Python 1.6 was the last of the versions released by CNRI. In 2000, Guido and the Python core development team moved to BeOpen.com to form the BeOpen PythonLabs team. Python 2.0 was the first and only release from BeOpen.com.

Following the release of Python 1.6, and after Guido van Rossum left CNRI to work with commercial software developers, it became clear that the ability to use Python with software available under the GNU Public License (GPL) was very desirable. CNRI and the Free Software Foundation (FSF) interacted to develop enabling wording changes to the Python license. Python 1.6.1 is essentially the same as Python 1.6, with a few minor bug fixes, and with a different license that enables later versions to be GPL-compatible. Python 2.1 is a derivative work of Python 1.6.1, as well as of Python 2.0.

After Python 2.0 was released by BeOpen.com, Guido van Rossum and the other PythonLabs developers joined Digital Creations. All intellectual property added from this point on, starting with Python 2.1 and its alpha and beta releases, is owned by the Python Software Foundation (PSF), a non-profit modeled after the Apache Software Foundation. See http://www.python.org/psf/ for more information about the PSF.

Thanks to the many outside volunteers who have worked under Guido's direction to make these releases possible.


B.B. Terms and conditions for accessing or otherwise using Python

B.B.1. PSF license agreement

  1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 2.1.1 software in source or binary form and its associated documentation.
  2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 2.1.1 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001 Python Software Foundation; All Rights Reserved" are retained in Python 2.1.1 alone or in any derivative version prepared by Licensee.
  3. In the event Licensee prepares a derivative work that is based on or incorporates Python 2.1.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 2.1.1.
  4. PSF is making Python 2.1.1 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.1.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
  5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.1.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.1.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
  6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
  7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
  8. By copying, installing or otherwise using Python 2.1.1, Licensee agrees to be bound by the terms and conditions of this License Agreement.

B.B.2. BeOpen Python open source license agreement version 1

  1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the Individual or Organization ("Licensee") accessing and otherwise using this software in source or binary form and its associated documentation ("the Software").
  2. Subject to the terms and conditions of this BeOpen Python License Agreement, BeOpen hereby grants Licensee a non-exclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use the Software alone or in any derivative version, provided, however, that the BeOpen Python License is retained in the Software, alone or in any derivative version prepared by Licensee.
  3. BeOpen is making the Software available to Licensee on an "AS IS" basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
  4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
  5. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
  6. This License Agreement shall be governed by and interpreted in all respects by the law of the State of California, excluding conflict of law provisions. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between BeOpen and Licensee. This License Agreement does not grant permission to use BeOpen trademarks or trade names in a trademark sense to endorse or promote products or services of Licensee, or any third party. As an exception, the "BeOpen Python" logos available at http://www.pythonlabs.com/logos.html may be used according to the permissions granted on that web page.
  7. By copying, installing or otherwise using the software, Licensee agrees to be bound by the terms and conditions of this License Agreement.

B.B.3. CNRI open source GPL-compatible license agreement

  1. This LICENSE AGREEMENT is between the Corporation for National Research Initiatives, having an office at 1895 Preston White Drive, Reston, VA 20191 ("CNRI"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 1.6.1 software in source or binary form and its associated documentation.
  2. Subject to the terms and conditions of this License Agreement, CNRI hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 1.6.1 alone or in any derivative version, provided, however, that CNRI's License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) 1995-2001 Corporation for National Research Initiatives; All Rights Reserved" are retained in Python 1.6.1 alone or in any derivative version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with Python 1.6.1 may be located on the Internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This Agreement may also be obtained from a proxy server on the Internet using the following URL: http://hdl.handle.net/1895.22/1013".
  3. In the event Licensee prepares a derivative work that is based on or incorporates Python 1.6.1 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 1.6.1.
  4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
  5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
  6. This License Agreement will automatically terminate upon a material breach of its terms and conditions.
  7. This License Agreement shall be governed by the federal intellectual property law of the United States, including without limitation the federal copyright law, and, to the extent such U.S. federal law does not apply, by the law of the Commonwealth of Virginia, excluding Virginia's conflict of law provisions. Notwithstanding the foregoing, with regard to derivative works based on Python 1.6.1 that incorporate non-separable material that was previously distributed under the GNU General Public License (GPL), the law of the Commonwealth of Virginia shall govern this License Agreement only as to issues arising under or with respect to Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between CNRI and Licensee. This License Agreement does not grant permission to use CNRI trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party.
  8. By clicking on the "ACCEPT" button where indicated, or by copying, installing or otherwise using Python 1.6.1, Licensee agrees to be bound by the terms and conditions of this License Agreement.

B.B.4. CWI permissions statement and disclaimer

Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, The Netherlands. All rights reserved.

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stichting Mathematisch Centrum or CWI not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.

STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.