Начало > В глубь языка Python | >> | ||||
![]() | В глубь языка Python Для программистов |
19 марта 2002
Copyright © 2000, 2001, 2002 Марк Пилгрим
Copyright © 2001, 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.
Содержание
Предисловие | ||
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python | >> | ||||
![]() | В глубь языка Python Для программистов |
19 марта 2002
Copyright © 2000, 2001, 2002 Марк Пилгрим
Copyright © 2001, 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.
Содержание
Предисловие | ||
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Предисловие | << >> | ||||
![]() | В глубь языка Python Для программистов |
Эта книга не предназначена для новичков. Подразумевается, что:
Если вы только начали изучать программирование, это не значит, что вы не можете изучать Python. Python — один из самых простых языков для обучения, но для начинающих предназначены совсем другие книги. Я настоятельно рекомендую Learning to Program и How to Think Like a Computer Scientist, а на сайте Python.org вы найдете ссылки на другие введения в язык программирования Python для не программистов.
В глубь языка Python | Знакомство с языком Python | |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python | << >> | ||||
![]() | В глубь языка Python Для программистов |
Содержание
Здесь представлена полноценная программа на языке Python.
Возможно эта программа не представляет для вас никакого смысла. Не беспокойтесь, мы проанализируем каждую строку. Но сначала прочитайте ее и посмотрите, в чем вы смогли разобраться самостоятельно.
Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.
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 3 4 5 6 7 8 9 10 11 12 13 14 15 | Объявление функций |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Объявление функций | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке Python, как и в большинстве других языков программирования, есть функции, но в нем нет отдельных заголовочных файлов, как в C++, или разделов интерфейс/реализация, как в языке Pascal. Если вам нужна функция — просто определите ее.
Здесь необходимо сделать несколько замечаний. Во-первых, определение функции начинается с ключевого слова def, после которого следуют имя функции и, в скобках, аргументы. В данном примере функция имеет только один аргумент, если же функция должна воспринимать несколько аргументов, они перечисляются через запятую.
Во-вторых, вы не определяете тип возвращаемого значения. В языке Python никогда не указывается не только тип возвращаемого значения, но даже его наличие. На самом деле каждая функция возвращает значение; если функция выполняет инструкцию return, она возвращает указанное в ней значени, иначе функция возвращает специальное значение — None.
![]() | |
В языке Visual Basic определение функций (возвращающих значение) начинается с ключевого слова function, а подпрограмм (не возвращающих значение) — с sub. В языке Python нет подпрограмм. То, что называется подпрограммой в других языках, в языке Python является функцией. Любая функция возвращает значение, даже если это None, и определение любой функции начинается с def. |
В третьих, не указан тип аргумента params. В языке Python тип переменных никогда не указывается явно. Python отслеживает тип объектов во время выполнения программы.
![]() | |
В Java, C++ и других языках со статической типизацией вы должны указывать тип возвращаемого значения и аргументов функции. В языке Python вы никогда не указываете тип. Тип переменной определяется, когда вы присваиваете ей значение. |
Дополнение. Эрудированный читатель прислал мне следуещее объяснение сравнения Python с другими языками программирования:
Таким образом Python является языком со строгой (тип переменной имеет значение) динамической (не используется объявление типа) типизацией.
Знакомство с языком Python | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Документирование функций |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Документирование функций | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке Python вы можете документировать функции, снабжая их строками документации.
Пример 1.4. Определение строки документации для buildConnectionString
def buildConnectionString(params): """Создает и возвращает строку соединения из словаря параметров."""
Утроенные кавычки используются для многострочных строковых литералов. Все, что находится между утроенными кавычками, является одним строковым значением, включая символы перехода на новую строку и другие символы кавычек. Вы можете использовать их где угодно, но чаще всего их употребляют для определения строк документации.
![]() | |
Утроенные кавычки являются удобным способом определения строк, содержащих одновременно одинарные и двойные кавычки, аналогично qq/.../ в Perl. |
Первая строка в определении функции (то есть первая после двоеточия) является строкой документации, в которой поясняется, что функция делает. Python не требует наличия строки документации у функции, но все же ее стоит всегда определять. Я знаю, вы слышали об этом на каждой лекции по программированию, но Python дает вам дополнительный стимул: строка документации доступна во время выполнения программы в качестве атрибута функции.
![]() | |
Многие Python IDE используют строки документации для контекстной справки. Например, во время ввода имени функции строка документации может отображаться в виде всплывающей подсказки. Такая помощь может быть очень полезной — все зависит от того, насколько хорошо написана строка документации. |
Дополнительная литература
Объявление функций | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Все является объектами |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Все является объектами | << >> | ||||
![]() | В глубь языка Python Для программистов |
Если вы не обратили внимание, я только что заметил, что функции в языке Python имеют атрибуты, и эти атрибуты доступны во время выполнения программы.
Функция, как и все остальное в языке Python, является объектом.
Пример 1.5. Доступ к строке документации функции buildConnectionString
>>> import odbchelper>>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"} >>> print odbchelper.buildConnectionString(params)
server=mpilgrim;uid=sa;database=master;pwd=secret >>> print odbchelper.buildConnectionString.__doc__
Создает и возвращает строку соединения из словаря параметров.
![]() | |
import в Python работает аналогично require в Perl. Проимпортировав в Python один раз модуль с помощью инструкции import вы можете обращаться к его функциям в виде module.function; проимпортировав модуль с помощью инструкции require в Perl, вы можете обращаться к его функциям в виде module::function. |
В языке Python все является объектами, и почти все из них имеют атрибуты и методы.[1] Все функции имеют специальный атрибут, который содержит строку документации, определенную в исходном коде.
Это настолько важно, что я повторю еще раз: в языке Python все является объектами. Строки являются объектами. Списки являются объектами. Функции являются объектами. И даже модули, как мы скоро увидим, являются объектами.
Дополнительная литература
Footnotes
[1] Различные языки определяют понятие “объект” по-разному. В некоторых языках все объекты должны иметь атрибуты и методы, в других — от любых объектов можно породить подклассы. Python определяет понятие объекта гораздо шире: некоторые объекты не имеют ни атрибутов, ни методов, и для всех объектов могут быть порождены подклассы (более подробно читайте об этом в главе 3). Однако любой объект может быть присвое переменной или использован в качестве аргумента функции. (более подробно читайте об этом в главе 2).
Документирование функций | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Отступы |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Отступы | << >> | ||||
![]() | В глубь языка Python Для программистов |
В определении функций нет явных 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Тестирование модулей |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Тестирование модулей | << >> | ||||
![]() | В глубь языка Python Для программистов |
Модули в языке Python также являются объектами и имеют несколько полезных атрибутов. Вы можете использовать их, например, для тестирования.
Несколько замечаний, перед тем как мы перейдем к главному. Во-первых, выражение в инструкции 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Словари |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Словари | << >> | ||||
![]() | В глубь языка Python Для программистов |
Сделаем небольшое отступление, так как вам необходимо познакомиться со словарями, кортежами и списками. Если вы знаток Perl, то уже имеете некоторое представление о словарях и списках, но вам, тем не менее, необходимо обратить внимание на кортежи.
Один из втроенных типов языка Python, словарь, сопоставляет ключам значения.
![]() | |
Словари в Python аналогичны хешам в Perl. В Perl имена переменных, хранящих хеши, всегда начинаются с символа %. Переменные в языке Python могут иметь произвольное имя, интерпретатор сам определяет их тип. |
![]() | |
Словари в Python схожи с экземплярами класса Hashtable в Java. |
![]() | |
Словари Python схожи с экземплярами объекта Scripting.Dictionary в Visual Basic. |
Пример 1.9. Определени словарей
>>> d = {"server":"mpilgrim", "database":"master"}>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["server"]
'mpilgrim' >>> d["database"]
'master' >>> d["mpilgrim"]
Traceback (innermost last): File "<interactive input>", line 1, in ? KeyError: mpilgrim
Пример 1.10. Изменение словарей
>>> d {'server': 'mpilgrim', 'database': 'master'} >>> d["database"] = "pubs">>> d {'server': 'mpilgrim', 'database': 'pubs'} >>> d["uid"] = "sa"
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'}
Обратите внимание, что новая запись (ключ "uid", значение "sa") появилась в середине. В самом деле, то что записи появились в том же порядке, в котором были введены — это случайность.
![]() | |
Словари не поддерживают порядок следования записей. Говорить, что порядок следования записей нарушается, некорректно — они просто не упорядочены. Эта важная особенность возможно будет раздражать, когда вы захотите получить записи в каком-то определенном воспроизводимом порядке (например, в алфавитном порядке ключей). Конечно, существует возможность это сделать, просто такая возможность не встроена в словарь. |
Пример 1.11. Смешивание различных типов данных в словарях
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'pubs'} >>> d["retrycount"] = 3>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d[42] = "douglas"
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3}
Пример 1.12. Удаление записей из словарей
>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 42: 'douglas', 'retrycount': 3} >>> del d[42]>>> d {'server': 'mpilgrim', 'uid': 'sa', 'database': 'master', 'retrycount': 3} >>> d.clear()
>>> d {}
Пример 1.13. Регистр букв в строках имеет значение
>>> d = {} >>> d["key"] = "value" >>> d["key"] = "other value">>> d {'key': 'other value'} >>> d["Key"] = "third value"
>>> d {'Key': 'third value', 'key': 'other value'}
Дополнительная литература
Тестирование модулей | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Списки |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Списки | << >> | ||||
![]() | В глубь языка Python Для программистов |
Список являются одним из самых используемых типов данных в языке Python. Если все ваше знакомство со списками ограничивается массивами в Visual Basic или (не дай бог) datastore в Powerbuilder, возмите себя в руки для знакомства со списками в языке Python.
![]() | |
Списки в языке Python похожи на массивы в языке Perl. Имена пееменных, хранящих массивы, в языке Perl всегда начинаются с символа @. Python не накладывает никаких дополнительных ограничений на имя переменных, в которых хранятся списки, интерпретатор сам определяет тип переменной. |
![]() | |
Списки в языке Python — нечто большее, чем массивы в Java (хотя вы можете использовать их в таком качестве, если это все, что вам требуется от жизни). Более близкой аналогией будет класс Vector, способный содержать произвольные объекты и динамически увеличиваться при добавлении новых элементов. |
Пример 1.14. Определение списков
>>> li = ["a", "b", "mpilgrim", "z", "example"]>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[0]
'a' >>> li[4]
'example'
Пример 1.15. Отрицательные индексы
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[-1]'example' >>> li[-3]
'mpilgrim'
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[1:3]['b', 'mpilgrim'] >>> li[1:-1]
['b', 'mpilgrim', 'z'] >>> li[0:3]
['a', 'b', 'mpilgrim']
Пример 1.17. Сокращения в записи среза
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li[:3]['a', 'b', 'mpilgrim'] >>> li[3:]
['z', 'example'] >>> li[:]
['a', 'b', 'mpilgrim', 'z', 'example']
Пример 1.18. Добавление элементов в список
>>> li ['a', 'b', 'mpilgrim', 'z', 'example'] >>> li.append("new")>>> li ['a', 'b', 'mpilgrim', 'z', 'example', 'new'] >>> li.insert(2, "new")
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new'] >>> li.extend(["two", "elements"])
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements']
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.index("example")5 >>> li.index("new")
2 >>> li.index("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.index(x): x not in list >>> "c" in li
0
![]() | |
Python не имеет отдельного булева типа. В булевом контексте (например, в условии инструкции if), 0 является ложью, а все остальные сисла являются истиной. Аналогично и для других типов: пустая строка (""), список ([]) и словарь ({}) являются ложью, а все остальные строки, списки и словари — истиной. |
Пример 1.20. Удаление элементов из списка
>>> li ['a', 'b', 'new', 'mpilgrim', 'z', 'example', 'new', 'two', 'elements'] >>> li.remove("z")>>> li ['a', 'b', 'new', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("new")
>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two', 'elements'] >>> li.remove("c")
Traceback (innermost last): File "<interactive input>", line 1, in ? ValueError: list.remove(x): x not in list >>> li.pop()
'elements' >>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two']
Пример 1.21. Применение операторов к спискам
>>> li = ['a', 'b', 'mpilgrim'] >>> li = li + ['example', 'new']>>> li ['a', 'b', 'mpilgrim', 'example', 'new'] >>> li += ['two']
>>> li ['a', 'b', 'mpilgrim', 'example', 'new', 'two'] >>> li = [1, 2] * 3
>>> li [1, 2, 1, 2, 1, 2]
Дополнительная литература
Словари | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Кортежи |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Кортежи | << >> | ||||
![]() | В глубь языка Python Для программистов |
Кортеж — это неизменяемый список. С момента создания кортеж не может быть изменен никакими способами.
Пример 1.22. Определение кортежей
>>> t = ("a", "b", "mpilgrim", "z", "example")>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t[0]
'a' >>> t[-1]
'example' >>> t[1:3]
('b', 'mpilgrim')
Пример 1.23. У кортежей нет методов
>>> t ('a', 'b', 'mpilgrim', 'z', 'example') >>> t.append("new")Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'append' >>> t.remove("z")
Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'remove' >>> t.index("example")
Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'index' >>> "z" in t
1
Так для чего же нужны кортежи?
![]() | |
Кортеж может быть преобразован в список и наоборот. Встроенная функция tuple воспринимает список в качестве аргумента и возвращает кортеж с теми же самыми элементами, и функция list воспринимает кортеж в качестве аргумента и возвращает список. В результате tuple “замораживает” список, а list его “размораживает”. |
Дополнительная литература
Footnotes
[2] На самом деле все несколько сложнее. Ключи словаря должны быть неизменяемыми. Кортежи сами по себе неизменяемы, но, если у вас имеется кортеж списков, то он считается изменяемым и не может быть использован в качесве ключа словаря. Только кортежи чисел, строк и других таких же кортежей могут быть ключами словаря.
Списки | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Определение переменных |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Определение переменных | << >> | ||||
![]() | В глубь языка Python Для программистов |
После того, как вы познакомились со словарями, кортежами и списками, давайте вернемся к нашему примеру программы — 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Присваивание сразу нескольких значений |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Присваивание сразу нескольких значений | << >> | ||||
![]() | В глубь языка Python Для программистов |
Одна из приятных возможностей языка Python — использование последовательностей для односременного присваивания нескольких значений.
Пример 1.26. Присваивание сразу нескольких значений
>>> v = ('a', 'b', 'e') >>> (x, y, z) = v>>> x 'a' >>> y 'b' >>> z 'e'
Использовать эту возможность можно по-разному. У меня часто возникает необходимость присвоить переменным диапазон значений. В языке C, вы бы использовали тип enum и вручную перечислили каждую константу и ассоциированное с ней значение, что утомительно, особенно, если значения идут подряд. Python позволяет использовать встроенную функцию range совместно с множественным присваиванием.
Пример 1.27. Присваивание идущих подряд значений
>>> range(7)[0, 1, 2, 3, 4, 5, 6] >>> (MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY) = range(7)
>>> MONDAY
0 >>> TUESDAY 1 >>> SUNDAY 6
Множественное присваивание также полезно при использовании функций, возвращающих несколько значений в виде кортежа. Вы просто присваиваете их отдельным переменным. Так поступают многие стандартные библиотеки языка Python, включая модуль os, который обсуждается в главе 3.
Дополнительная литература
Определение переменных | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Форматированное представление |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Обработка списков | << >> | ||||
![]() | В глубь языка Python Для программистов |
Одна из самых мощных особенностей языка Python — расширенная запись списков, которая позволяет легко преобразовать один список в другой, применяя к каждому элементу функцию.
Пример 1.30. Введение в расширенную запись списков
>>> li = [1, 9, 8, 4] >>> [elem*2 for elem in li][2, 18, 16, 8] >>> li
[1, 9, 8, 4] >>> li = [elem*2 for elem in li]
>>> li [2, 18, 16, 8]
Пример 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()['server', 'uid', 'database', 'pwd'] >>> params.values()
['mpilgrim', 'sa', 'master', 'secret'] >>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
Давайте теперь посмотрим, что делает функция 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()]['server', 'uid', 'database', 'pwd'] >>> [v for k, v in params.items()]
['mpilgrim', 'sa', 'master', 'secret'] >>> ["%s=%s" % (k, v) for k, v in params.items()]
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
![]() | Обратите внимание, что мы используем две переменные при переборе элементов списка params.items(). Это — еще один пример использования множественного присваивания. Первым элементом списка params.items() является кортеж ('server', 'mpilgrim'), поэтому для первой итерации k получит значение 'server' и v получит значение 'mpilgrim'. В данном случае мы игнорируем v и включаем в результат только k, так что в результате мы получим список, эквивалентный params.keys(). |
![]() | Здесь мы делаем то же самое, но игнорируем k, так что в результате мы получим список, эквивалентный params.values(). |
![]() | Комбинируя эти два примера с простым форматированием, мы получаем список строк, содержащих ключ и значение для каждой записи словаря. Результат получается подозрительно похожим на вывод программы, все что осталось сделать — это объединить все элементы списка в одну строку. |
Дополнительная литература
Форматированное представление | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Объединение и разбиение строк |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Форматированное представление | << >> | ||||
![]() | В глубь языка Python Для программистов |
Python позволяет получить форматированное представление значений в виде строки. Хотя строка формата может содержать довольно сложные выражения, чаще всего используется вставка значений в строку с помощью шаблона %s.
![]() | |
Python использует для строк формата такой же синтаксис, как и функция sprintf в C. |
Обратите внимание, что (k, v) является кортежем. Я уже говорил, что кортежи весьма полезный тип данных.
Вы можете подумать, что здесь слишком много работы для простого объединения строк. И вы будете правы, но форматирование — это не просто объединение строк. Это даже не просто форматирование. Данная операция также выполняет приведение типа.
Пример 1.29. Форматирование vs. объединение
>>> uid = "sa" >>> pwd = "secret" >>> print pwd + " is not a good password for " + uidsecret is not a good password for sa >>> print "%s is not a good password for %s" % (pwd, uid)
secret is not a good password for sa >>> userCount = 6 >>> print "Users connected: %d" % (userCount, )
![]()
Users connected: 6 >>> print "Users connected: " + userCount
Traceback (innermost last): File "<interactive input>", line 1, in ? TypeError: cannot add type "int" to string
Дополнительная литература
Присваивание сразу нескольких значений | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Обработка списков |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Знакомство с языком Python > Объединение и разбиение строк | << >> | ||||
![]() | В глубь языка Python Для программистов |
Вы имеете список строк с парами ключ-значение вида 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.
>>> li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s = ";".join(li) >>> s 'server=mpilgrim;uid=sa;database=master;pwd=secret' >>> s.split(";")['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret'] >>> s.split(";", 1)
['server=mpilgrim', 'uid=sa;database=master;pwd=secret']
![]() | |
Вызов anystring.split(delimiter, 1) может быть полезен, если вы хотите найти первое вхождение подстроки и, затем, работать с фрагментами до этой подстроки (он является первым элементом возвращаемого списка) и после него (второй элемент возвращаемого списка). |
Дополнительная литература
Обработка списков | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Заключение |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Теперь программа 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Сила самоанализа |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License | << >> | ||||
![]() | В глубь языка Python Для программистов |
Содержание
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.
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.
Summary | 1 2 3 4 5 6 7 8 9 10 11 12 | Applicability and definitions |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Summary | << >> | ||||
![]() | В глубь языка Python Для программистов |
The fileinfo.py program should now make perfect sense.
"""Получение метаинформации, специфичной для файла данного типа. Создайте экземпляр соответствующего класса, передав конструктору имя файла. Возвращаемый объект ведет себя аналогично словарю с парами ключ-значение для каждой части метаинформации. 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:
Putting it all together | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | GNU Free Documentation License |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования | << >> | ||||
![]() | В глубь языка Python Для программистов |
Содержание
Начиная с этой главы мы будем иметь дело с объектно ориентированным программированием на языке Python. Помните, я говорил, что вам необходимо знать объектно-ориентированный язык для чтения этой книги? Так я не шутил.
Здесь приведена полноценная программа на языке Python. Прочитайте строки документации модуля, классов и функций для полуения информации о том, что программа делает и как она работает. Как обычно, не обеспокойтесь, если что-либо вы не понимаете: для этого и предназначена оставшаяся часть главы.
Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.
"""Получение метаинформации, специфичной для файла данного типа. Создайте экземпляр соответствующего класса, передав конструктору имя файла. Возвращаемый объект ведет себя аналогично словарю с парами ключ-значение для каждой части метаинформации. 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
Пример 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
Заключение | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Импортирование модулей инструкцией from module import |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Заключение | << >> | ||||
![]() | В глубь языка Python Для программистов |
Теперь код программы 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
Перед тем как перейти к следующей главе убедитесь, что вы овладели следующими навыками:
Объединяем все вместе | 1 2 3 4 5 6 7 8 9 | Средства объектно-ориентированного программирования |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа | << >> | ||||
![]() | В глубь языка Python Для программистов |
Содержание
Эта глава описывает одну из самых сильных возможностей языка Python — самоанализ. Ка вы уже знаете, все в языке Python является объектами. Самоанализ — использование специального кода для просмотра в памяти модулей и функций как объектов, извлекая информацию о них и о том, как их использовать. По ходу мы будем определять функции без имени, передавать аргументы в неправильном порядке и использовать функции, имена которых до этого даже не знали.
Ниже приведена законченная работающая программа на языке Python. Вы должны понять большую часть кода просто просмотрев ее. Пронумерованные строки используют концепции, описанные в главе Знакомство с языком Python. Не беспокойтесь, если оставшаяся часть кода выглядит пугающе, в этой главе вы узнаете все, что необходимо для его понимания.
Если вы еще этого не сделали, можете загрузить этот и другие примеры, используемые в книге.
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__
![]() | Этот модуль определяет одну функцию — help. Она воспринимает три аргумента: object, spacing и collapse. Последние два аргумента, как мы скоро увидим, не являются обязательными. |
![]() | Функция help имеет многострочную строку документации, кратко описывающую предназначение функции. Обратите внимание, что здесь не упомянуто возвращаемое значение — функция предназначениа для выполнения определенных действий а не возврата значения. |
![]() | Тело функции выделено отступами. |
![]() | Прием с if __name__ позволяет использовать модуль в качестве программы без каких-либо конфликтов. В данном случае программа просто выводит строку документации функции help. |
![]() | В инструкции 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.
Заключение | 1 2 3 4 5 6 7 8 9 | Необязательные и именованные аргументы |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Объединяем все вместе | << >> | ||||
![]() | В глубь языка Python Для программистов |
Последняя строка кода и единственная еще не разобранная — делает всю работу. Но теперь задача совсем проста, так как все, что нам необходимо, уже готово. Все костяшки домино на месте, осталось только толкнуть их.
Пример 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>>> method = 'buildConnectionString'
>>> getattr(object, method)
<function buildConnectionString at 010D6D74> >>> print getattr(object, method).__doc__
Создает и возвращает строку соединения из словаря параметров.
![]() | В функцию help object — объект, для которого мы хотим получить помощь — передается в качестве аргумента. |
![]() | По ходу того, как мы пробегаем по списку methodList, method содержит имя текущего метода. |
![]() | С помощью функции getattr мы получаем сам метод method объекта object. |
![]() | Теперь осталось самое простое — распечатать строку документации метода. |
Следующий кирпичик — использование функции str. Как вы уже знаете, str — встроенная функция, перобразующая объект к строке. Но строка документации уже является строкой, так зачем же суетиться с использованием функции str? На самом деле не каждая функция имеет строку документации, и если строки документации нет, ее атрибут __doc__ равен None.
Пример 2.27. Зачем использовать str для строк документации?
>>> {}.keys.__doc__>>> {}.keys.__doc__ == None
1 >>> str({}.keys.__doc__)
'None'
![]() | |
В SQL вы должны использовать IS NULL вместо = NULL для сравнения с пустым значением. В языке Python вы можете использовать как == None, так и is None, но второй вариант работает быстрее. |
Теперь, когда мы уверены в том, что значение всегда будет строковым, можно передать его ранее определенной функции processFunc, которая сворачивает символы пропуска или возвращает строку без изменений. Теперь вы видите, почему важно использовать функцию str для преобразования None к строке. processFunc считает, что ее аргумент является строкой и вызывает его метод split. При попытке вызвать processFunc для None будет сгенерировано исключение, так как у None нет метода split.
Далее мы снова используем форматирование, чтобы соединить значение, возвращаемое функцией processFunc, со результатом применения метода ljust к строке method. Это новый метод строковых объектов, который мы раньше не видели.
>>> s = 'buildConnectionString' >>> s.ljust(30)'buildConnectionString ' >>> s.ljust(20)
'buildConnectionString'
Ну вот и почти все сделано. Имея метод ljust, дополняющий имя метода пробелами до нужной длины, и строку документации (возможно со свернутыми символами пропуска), которую возвращает processFunc, мы объединяем их и получаем одну строку. Так как мы пробегаем по списку methodList, то получим список строк. Используя метод join строки "\n", мы объединяем строки в одну с символом перехода на новую строку в качестве разделителя и печатаем результат.
Теперь код обрел смысл.
Использование lambda-функций | 1 2 3 4 5 6 7 8 9 | Заключение |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Необязательные и именованные аргументы | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке 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)help(odbchelper, 12)
help(odbchelper, collapse=0)
help(spacing=15, object=odbchelper)
Это выглядит странным, пока вы не увидите, что список значений аргументов фактически является словарем. “Обычный” же способ передачи аргументов без указания их имен является сокращенной записью: интерпретатор сопоставляет значения аргументов их именам в соответствии с порядком, в котором они были указаны в определении функции. В большинстве случаев вы вызываете функции “обычным” способов, но при необходимости всегда можете воспользоваться дополнительной возможностью.
![]() | |
Все что Вам нужно сделать для вызова функции — это указать значение для каждого обязательного аргумента. Способ передачи аргументов и порядок их следования — дело Вашего вкуса. |
Дополнительная литература
Сила самоанализа | 1 2 3 4 5 6 7 8 9 | type, str, dir и другие встроенные функции |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > type, str, dir и другие встроенные функции | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке Python есть небольшой набор очень полезных встроенных функций. Все остальные функции распределены по модулям. В самом деле, это удачное проектное решение позволяет предотвратить разбухание ядра языка, как это произошло с некоторыми другими скриптовыми языками (например, Visual Basic).
Функция type возвращает тип произвольного объекта. Возможные значения типов перечислены в модуле types. Определение типа полезно в функциях, способных обрабатывать данные нескольких типов.
>>> type(1)<type 'int'> >>> li = [] >>> type(li)
<type 'list'> >>> import odbchelper >>> type(odbchelper)
<type 'module'> >>> import types
>>> type(odbchelper) == types.ModuleType 1
Функция str преобразует данные в строку. Для любого типа данных можно получить строковое представление.
>>> str(1)'1' >>> horsemen = ['war', 'pestilence', 'famine'] >>> horsemen.append('Powerbuilder') >>> str(horsemen)
"['war', 'pestilence', 'famine', 'Powerbuilder']" >>> str(odbchelper)
"<module 'odbchelper' from 'c:\\docbook\\dip\\py\\odbchelper.py'>" >>> str(None)
'None'
Сердцем функции help является мощная функция dir. dir возвращает список атрибутов и методов произвольного объекта: модуля, функции, строки, списка, словаря… в общем, любого объекта.
>>> li = [] >>> dir(li)['append', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort'] >>> d = {} >>> dir(d)
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'setdefault', 'update', 'values'] >>> import odbchelper >>> dir(odbchelper)
['__builtins__', '__doc__', '__file__', '__name__', 'buildConnectionString']
![]() | li является списком, так что dir(li) дает список имен всех методов списка. Обратите внимание, что возвращаемый список содержит имена методов в виде строк, а не сами методы. |
![]() | d является словарем, поэтому dir(d) дает список имен методов словаря. Как минимум один из них, keys, вам уже знаком. |
![]() | Здесь начинается самое интересное. odbchelper является модулем, так что dir(odbchelper) дает список всех имен, определенных в модуле, включая специальные атрибуты, такие как __name__ и __doc__. В данном случае, odbchelper содержит одну пользовательскую функцию, buildConnectionString, которую мы изучали в главе Знакомство с языком Python. |
Наконец, функция callable возвращает 1, если аргумент может быть вызван, в противном случае возвращает 0. Вызов поддерживают такие объекты, как функции, методы и даже классы. (Более подробно о классах читайте в главе 3.)
>>> import string >>> string.punctuation'!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' >>> string.join
<function join at 00C55A7C> >>> callable(string.punctuation)
0 >>> callable(string.join)
1 >>> print string.join.__doc__
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)
![]() | На смену большинству функций модуля string пришли методов строк (хотя многие до сих пор используют функцию join), но модуль также содержит множество полезных констант. Например string.punctuation содержит все стандартные символы пунктуации. |
![]() | Функция string.join объединяет строки списка в одну строку. |
![]() | string.punctuation является строкой и вызов не поддерживает. (Строки имеют методы, которые можно вызвать, но вы не можете вызвать саму строку.) |
![]() | string.join ялвляется функцией и поддерживает вызов с двумя аргументами. |
![]() | Любой объект, поддерживающий вызов, может иметь строку документации. Применяя функцию 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 в основном содержит эту документацию в себе. |
Дополнительная литература
Необязательные и именованные аргументы | 1 2 3 4 5 6 7 8 9 | Получение атрибутов с помощью getattr |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Получение атрибутов с помощью getattr | << >> | ||||
![]() | В глубь языка Python Для программистов |
Вы уже знаете, что функции в языке Python являются объектами. Но вы пока не знаете, что если имя функции становится известно только во время выполнения программы, то ее можно получить с помощью функции getattr.
>>> li = ["Larry", "Curly"] >>> li.pop<built-in method pop of list object at 010DF884> >>> getattr(li, "pop")
<built-in method pop of list object at 010DF884> >>> getattr(li, "append")("Moe")
>>> li ["Larry", "Curly", "Moe"] >>> getattr({}, "clear")
<built-in method clear of dictionary object at 00F113D4> >>> getattr((), "pop")
Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'tuple' object has no attribute 'pop'
![]() | Таким образом вы получаете метод pop списка. Обратите внимание, что вы не вызываете его (для этого нужно выполнить li.pop()), а получаете в виде объекта. |
![]() | Здесь мы также получаем метод pop, но теперь имя метода указано в виде строкового аргумента функции getattr. getattr — очень полезная функция, позволяющая получить любой атрибут любого объекта. В данном случае объектом является список, а его атрибутом — метод pop. |
![]() | В случае, если вы еще не до конца осознали, насколько это может быть полезным, попробуйте выполнить этот код: значение, возвращаемое функцией getattr является методом, который можно вызвать, как если бы вы просто вызвали li.append("Moe"). Но вы не вызываете метод напрямую — вы указываете имя метода в виде строки. |
![]() | getattr работает и для словарей. |
![]() | Теоретически, getattr работает и для кортежей, но кортежи не имеют методов, так что getattr сгенерирует исключение независимо от имени атрибута, которое вы дадите. |
getattr предназначен не только для встроенных типов данных. Он также работает и для модулей.
Пример 2.12. getattr в apihelper.py
>>> import odbchelper >>> odbchelper.buildConnectionString<function buildConnectionString at 00D18DD4> >>> getattr(odbchelper, "buildConnectionString")
<function buildConnectionString at 00D18DD4> >>> object = odbchelper >>> method = "buildConnectionString" >>> getattr(object, method)
<function buildConnectionString at 00D18DD4> >>> type(getattr(object, method))
<type 'function'> >>> import types >>> type(getattr(object, method)) == types.FunctionType 1 >>> callable(getattr(object, method))
1
![]() | Так мы можем получить функцию buildConnectionString из модуля odbchelper, который сы изучали в разделе Знакомство с языком Python. (Шестнадцатиричный адрес характерен для моей машины, у вас он будет другим.) |
![]() | Используя getattr, мы можем получить ту же самую функцию. В общем, getattr(object, "attribute") дает такой же результат, как object.attribute. Если object является модулем, attribute может быть любым объектом, определенном в этом модуле: функцией, классом или другой глобальной переменной. |
![]() | На самом деле именно это и делает функция help. object передается функции в качестве аргумента; method является строкой с именем метода или функции. |
![]() | В данном случае, method содержит имя функции, в чем мы можем убедиться получив ее тип. |
![]() | Так как method является функцией, ее можно вызвать. |
type, str, dir и другие встроенные функции | 1 2 3 4 5 6 7 8 9 | Фильтрование списков |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Фильтрование списков | << >> | ||||
![]() | В глубь языка Python Для программистов |
Как вы уже знаете, 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]['mpilgrim', 'foo'] >>> [elem for elem in li if elem != "b"]
['a', 'mpilgrim', 'foo', 'c', 'd', 'd'] >>> [elem for elem in li if li.count(elem) == 1]
['a', 'mpilgrim', 'foo', 'c']
Пример 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__.
Дополнительная литература
Получение атрибутов с помощью getattr | 1 2 3 4 5 6 7 8 9 | Особенности операторов and и or |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Особенности операторов and и or | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке Python операторы and и or, как вы и ожидали, выполняют булевы операции, но они не возвращают булевы значения: результатом всегда является значение одного из операндов.
>>> 'a' and 'b''b' >>> '' and 'b'
'' >>> 'a' and 'b' and 'c'
'c'
![]() | При использовании оператора and, значения вычисляются в булевом контексте слева напрво. Значения 0, '', [], (), {} и None являются ложью, все остальное является истиной[3]. Если у and оба операнда являются истиной, результатом будет последнее значение. В данном случае вычисляется выражение 'a', которое является истиной, затем 'b', которое также является истиной, и возвращается 'b'. |
![]() | Если какой-либо из операндов является ложью, результатом будет первое такое значение. В данном случает '' — первое значение, являющееся ложью. |
![]() | Все значения являются истиной, так что в результате мы получаем последнее — 'c'. |
>>> 'a' or 'b''a' >>> '' or 'b'
'b' >>> '' or [] or {}
{} >>> def sidefx(): ... print "in sidefx()" ... return 1 >>> 'a' or sidefx()
'a'
Если вы используете C, то, наверное, знакомы с выражением bool ? a : b, которое дает a, если bool является истиной, b, если bool ложно. Благодаря особенностям работы операторов and и or в Python, вы можете достигнуть анологичного эффекта.
Однако, так как такое выражение выполняет обычные логические операции, а не является специальной конструкцией языка, существует очень важная разница между приемом с and-or в языке Python и конструкцией bool ? a : b в C. Если значение a является ложью, это прием не будет работать так как вы могли ожидать. (Вы можете сказать, что обожгись на этом? Более одного раза?)
![]() | |
Прием с 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]''
Кажется, этот прием не стоит того, чтобы его использовать. В конце концов вы всегда можете воспользоваться интсрукцией if. Тогда зачем вся эта суета? Конечно, в большинстве случаев выбирая между двумя константами вы можете использовать простой синтаксис и не беспокоиться, потому что значение a всегда будет истинным. И даже если придется использовать более сложный синтакс, на это могут найтись весские причины: в некоторых случаях в языке Python использование инструкции if не допускается, например в lambda-функциях.
Дополнительная литература
Footnotes
[3] Ну, почти все. По умолчанию экземпляры классов являются истиной, но вы можете определить специальные методы в классе, чтобы его экземпляры могли быть ложью. Вы узнаете все о классах и специальных методах в главе 3.
Фильтрование списков | 1 2 3 4 5 6 7 8 9 | Использование lambda-функций |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Сила самоанализа > Использование lambda-функций | << >> | ||||
![]() | В глубь языка Python Для программистов |
Python поддерживает интересный синтаксис, позволяющий определять небольшие однострочные функции на лету. Позаимствованные из Lisp, так назыаемые lambda-функции могут быть использованы везде, где требуется функция.
>>> def f(x): ... return x*2 ... >>> f(3) 6 >>> g = lambda x: x*2>>> g(3) 6 >>> (lambda x: x*2)(3)
6
Обобщая, 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">>> print s this is a test >>> print s.split()
['this', 'is', 'a', 'test'] >>> print " ".join(s.split())
'this is a test'
![]() | Это строка, которая содержит символ переход на новую строку, записанный в виде специальной последовательности (такие строки могут быть также записаны с использованием утроенных кавычек). \n — переход на новую строку, \t — символ горизонтальной табуляции. |
![]() | Метод split без аргументов разбивает строку по символам пропуска. В данном случае три пробела, переход на новую строку и табуляция воспринимаются одинаково. |
![]() | Вы можете нормализовать пропуски разбив строку, а затем снова объединив ее, используя один пробул в качестве разделителя. Именно это делает функция 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-функцию, которая будет делать только то, что вам нужно.
Дополнительная литература
Особенности операторов and и or | 1 2 3 4 5 6 7 8 9 | Объединяем все вместе |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Определение классов | << >> | ||||
![]() | В глубь языка Python Для программистов |
Python имеет полноценную поддержку объектно-ориентированного программирования: вы божете определять собственные классы, наследоваться от встроенных и собственных классов, создавать экземпляры определенных вами классов.
Определять классы в языке Python просто. Как и для функций, для классов нет отдельного определения интерфейса. Определения класса в языке Python начинается с зарезервированного слова class и следующего за ним имени класса. Технически, это все что требуется, так как класс совсем необязательно должен быть производным от другого класса.
class foo:pass
![]()
![]() | |
Инструкция pass в Python ведет себя аналогично пустым фигурным скобкам ({}) в Java и C. |
Конечно, в реальных программах большинство классов будут производными от других классов и будут определять собственные атрибуты и методы. Но, как вы уже увидели, нет ничего, что класс обязательно должен иметь, кроме имени. В частности, программистам на C++ может показаться странным, что классы в языке Python не имеют явных конструкторов и деструкторов. В классах языка Python есть нечто? похожее на конструктор — метод __init__.
Пример 3.6. Определение класса FileInfo
from UserDict import UserDict class FileInfo(UserDict):
![]() | В языке Python родительские классы просто перечисляются в скобках сразу после имени класса. В данном случае класс FileInfo наследуется от класса UserDict (который был проимпортирован из модуля UserDict). UserDict — класс, который ведет себя аналогично словарю, позволяя от него наследоваться и изменять или дополнять его поведение. (Существуют аналогичные классы UserList и UserString, позволяющие определить класс, производный от списка и строки.) Здесь есть немного черной магии, которую мы раскроем позже в этой главе, когда будем подробнее исследовать класс UserDict. |
![]() | |
В языке Python родительские классы просто перечисляются в скобках после имени. Для этого не нужно использовать специальное ключевое слово, такое как extends в Java. |
![]() | |
Хотя я не буду буду рассказывать об этом подробно, Python поддерживает множественное наследование. В скобках после имени класса вы можете пересислить через запятую столько родительских классов, сколько вам нужно. |
Пример 3.7. Инициализация класса FileInfo
class FileInfo(UserDict): "хранит метаинформацию о файле"def __init__(self, filename=None):
![]()
![]()
![]() | Для классов можно (и желательно) определять строку документации, также как для модулей и функций. |
![]() | Метод __init__ вызывается сразу после создания экземпляра класса. Соблазнительно, но не правильно называть этот метод конструктором. Соблазнительно, потому что он выглядит как конструктор (принято, чтобы __init__ был первым методом, определенным в классе), ведет себя как коструктор (это перый кусок кода, вызываемый в созданном экземпляре класса) и даже называется как коструктор. Неправильно, так как к тому времени, когда вызывается метод __init__, объект уже создан и вы имеете ссылку на созданный экземпляр класса. Но метод __init__ — это самое близкое к конструктору, из того что есть в языке Python. |
![]() | Первым аргументом каждого метода класса, включая __init__, всегда является текущий экземпляр класса. Общепринято всегда называть этот аргумент self. В методе __init__ self ссылается на только что созданный объект, в других методах — на экземпляр класса, для которого метод вызывается. Хотя и необходимо явно указывать self при определении метода, вы его не указываете, когда вызываете метод; Python добавит его автоматически. |
![]() | Метод __init__ может иметь несколько аргументов. Аргументы могут иметь значения по умолчанию, что сделает их необязательными. В данном случае аргумент filename имеет значение по умолчанию None. |
![]() | |
Первый аргумент метода класса (ссылка на текущий экземпляр) принято называть self. Этот аргумент играет роль зарезервированного слова this в C++ и Java, но self не является зарезервированным словом — просто соглашение. Несмотря на это, не стоит называть его иначе, чем self. |
Пример 3.8. Тело класса FileInfo
class FileInfo(UserDict): "хранит метаинформацию о файле" def __init__(self, filename=None): UserDict.__init__(self)self["name"] = filename
![]()
![]() | |
В определении методов необходимо явно указывать 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
Импортирование модулей инструкцией from module import | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Instantiating classes |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Импортирование модулей инструкцией from module import | << >> | ||||
![]() | В глубь языка Python Для программистов |
В языке Python есть два способа импортировать модули. Оба из них полезны, и вы должны знать, когда каждый из них лучше использовать. С одним способом, import module, вы уже ознакомились в главе 1. Второй способ делает примерно то же самое, но в его работе есть несколько важных отличий.
Синтакис очень похож на уже знакомый вам ситаксис 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<type 'function'> >>> FunctionType
Traceback (innermost last): File "<interactive input>", line 1, in ? NameError: There is no variable named 'FunctionType' >>> from types import FunctionType
>>> FunctionType
<type 'function'>
Когда следует использовать from module import?
Во всех остальных случаях — это дело вкуса. Вы увидите код на языке Python с использованием обоих вариантов.
Дополнительная литература
Средства объектно-ориентированного программирования | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Определение классов |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Instantiating classes | << >> | ||||
![]() | В глубь языка Python Для программистов |
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")>>> f.__class__
<class fileinfo.FileInfo at 010EC204> >>> f.__doc__
'base class for file info' >>> f
{'name': '/music/_singles/kairo.mp3'}
![]() | 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. |
![]() | 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__. |
![]() | 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. |
![]() | 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')... >>> for i in range(100): ... 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
Определение классов | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | UserDict: a wrapper class |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > UserDict: a wrapper class | << >> | ||||
![]() | В глубь языка Python Для программистов |
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:def __init__(self, dict=None):
self.data = {}
if dict is not None: self.update(dict)
![]()
![]() | Note that UserDict is a base class, not inherited from any other class. |
![]() | 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. |
![]() | 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. |
![]() | 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()def copy(self):
if self.__class__ is UserDict:
return UserDict(self.data) import copy
return copy.copy(self) def keys(self): return self.data.keys()
def items(self): return self.data.items() def values(self): return self.data.values()
![]() | 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.) |
![]() | 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. |
![]() | 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. |
![]() | 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. |
![]() | The rest of the methods are straightforward, redirecting the calls to the built-in methods on self.data. |
Further reading
Instantiating classes | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Special class methods |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Special class methods | << >> | ||||
![]() | В глубь языка Python Для программистов |
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")'/music/_singles/kairo.mp3' >>> f["name"]
'/music/_singles/kairo.mp3'
![]() | 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. |
![]() | 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)>>> f {'name':'/music/_singles/kairo.mp3', 'genre':31} >>> f["genre"] = 32
>>> f {'name':'/music/_singles/kairo.mp3', 'genre':32}
__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):if key == "name" and item:
self.__parse(item)
FileInfo.__setitem__(self, key, item)
![]() | 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.) |
![]() | 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. |
![]() | 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. |
![]() | 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()>>> mp3file {'name':None} >>> mp3file["name"] = "/music/_singles/kairo.mp3"
>>> 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"
>>> mp3file {'album': '', 'artist': 'The Cynic Project', 'genre': 18, 'title': 'Sidewinder', 'name': '/music/_singles/sidewinder.mp3', 'year': '2000', 'comment': 'http://mp3.com/cynicproject'}
![]() | 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. |
![]() | 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. |
![]() | Modifying the name key will go through the same process again: Python calls __setitem__, which calls self.__parse, which sets all the other keys. |
UserDict: a wrapper class | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Advanced special class methods |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Advanced special class methods | << >> | ||||
![]() | В глубь языка Python Для программистов |
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)def __cmp__(self, dict):
if isinstance(dict, UserDict): return cmp(self.data, dict.data) else: return cmp(self.data, dict) def __len__(self): return len(self.data)
def __delitem__(self, key): del self.data[key]
![]() | __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. |
![]() | __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. |
![]() | __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. |
![]() | __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
Special class methods | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Class attributes |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Class attributes | << >> | ||||
![]() | В глубь языка Python Для программистов |
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<class fileinfo.MP3FileInfo at 01257FDC> >>> fileinfo.MP3FileInfo.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>)} >>> m = fileinfo.MP3FileInfo()
>>> 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>)}
![]() | |
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... def __init__(self) ... self.__class__.count += 1
... >>> counter <class __main__.counter at 010EAECC> >>> counter.count
0 >>> c = counter() >>> c.count
1 >>> counter.count 1 >>> d = counter()
>>> d.count 2 >>> c.count 2 >>> counter.count 2
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.
Advanced special class methods | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Private functions |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Private functions | << >> | ||||
![]() | В глубь языка Python Для программистов |
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")Traceback (innermost last): File "<interactive input>", line 1, in ? AttributeError: 'MP3FileInfo' instance has no attribute '__parse'
![]() | 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.
Class attributes | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Handling exceptions |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Handling exceptions | << >> | ||||
![]() | В глубь языка Python Для программистов |
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")Traceback (innermost last): File "<interactive input>", line 1, in ? IOError: [Errno 2] No such file or directory: '/notthere' >>> try: ... fsock = open("/notthere")
... except IOError:
... print "The file does not exist, exiting gracefully" ... print "This line will always print"
The file does not exist, exiting gracefully This line will always print
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, TERMIOSexcept ImportError: try: import msvcrt
except ImportError: try: from EasyDialogs import AskPassword
except ImportError: getpass = default_getpass
else:
getpass = AskPassword else: getpass = win_getpass else: getpass = unix_getpass
Further reading
Footnotes
[6] Or, as some marketroids would put it, your program would perform an illegal action. Whatever.
Private functions | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | File objects |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > File objects | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
>>> f = open("/music/_singles/kairo.mp3", "rb")>>> f
<open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988> >>> f.mode
'rb' >>> f.name
'/music/_singles/kairo.mp3'
![]() | 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.) |
![]() | The open function returns an object (by now, this should not surprise you). A file object has several useful attributes. |
![]() | The mode attribute of a file object tells you what mode the file was opened in. |
![]() | The name attribute of a file object tells you the name of the file that the file object has open. |
>>> f <open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988> >>> f.tell()0 >>> f.seek(-128, 2)
>>> f.tell()
7542909 >>> tagData = f.read(128)
>>> tagData 'TAGKAIRO****THE BEST GOA ***DJ MARY-JANE*** Rave Mix 2000http://mp3.com/DJMARYJANE \037' >>> f.tell()
7543037
>>> f <open file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988> >>> f.closed0 >>> f.close()
>>> f <closed file '/music/_singles/kairo.mp3', mode 'rb' at 010E3988> >>> f.closed 1 >>> f.seek(0)
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()
![]() | 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. |
![]() | 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. |
![]() | 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. |
![]() | 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:fsock = open(filename, "rb", 0)
try: fsock.seek(-128, 2)
tagdata = fsock.read(128)
finally:
fsock.close() . . . except IOError:
pass
![]() | 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.) |
![]() | The open function may raise an IOError. (Maybe the file doesn't exist.) |
![]() | The seek method may raise an IOError. (Maybe the file is smaller than 128 bytes.) |
![]() | 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.) |
![]() | 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. |
![]() | 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
Handling exceptions | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | for loops |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > for loops | << >> | ||||
![]() | В глубь языка Python Для программистов |
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:... print s
a b e >>> print "\n".join(li)
a b e
![]() | 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. |
![]() | Like an if statement or any other indented block, a for loop can have any number of lines of code in it. |
![]() | 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. |
>>> for i in range(5):... print i 0 1 2 3 4 >>> li = ['a', 'b', 'c', 'd', 'e'] >>> for i in range(len(li)):
... print li[i] a b c d e
![]() | 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. |
![]() | 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():![]()
... 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()])
USERPROFILE=C:\Documents and Settings\mpilgrim OS=Windows_NT COMPUTERNAME=MPILGRIM USERNAME=mpilgrim [...snip...]
![]() | 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. |
![]() | 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. |
![]() | 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)}. . . if tagdata[:3] == "TAG": for tag, (start, end, parseFunc) in self.tagDataMap.items():
self[tag] = parseFunc(tagdata[start:end])
![]() | 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. |
![]() | 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. |
![]() | 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. |
File objects | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | More on modules |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > More on modules | << >> | ||||
![]() | В глубь языка Python Для программистов |
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>>> print '\n'.join(sys.modules.keys())
win32api os.path os exceptions __main__ ntpath nt sys __builtin__ site signal UserDict stat
Пример 3.32. Using sys.modules
>>> import fileinfo>>> 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"]
<module 'fileinfo' from 'fileinfo.pyc'>
Пример 3.33. The __module__ class attribute
>>> from fileinfo import MP3FileInfo >>> MP3FileInfo.__module__'fileinfo' >>> sys.modules[MP3FileInfo.__module__]
<module 'fileinfo' from 'fileinfo.pyc'>
![]() | Every Python class has a built-in class attribute __module__, which is the name of the module in which the class is defined. |
![]() | 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__]):"оределяет класс, предназначеный для обработки файла, по расширению" subclass = "%sFileInfo" % os.path.splitext(filename)[1].upper()[1:]
return hasattr(module, subclass) and getattr(module, subclass) or FileInfo
![]() | 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. |
![]() | 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. |
![]() | 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
for loops | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | The os module |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > The os module | << >> | ||||
![]() | В глубь языка Python Для программистов |
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")![]()
'c:\\music\\ap\\mahadeva.mp3' >>> os.path.join("c:\\music\\ap", "mahadeva.mp3")
'c:\\music\\ap\\mahadeva.mp3' >>> os.path.expanduser("~")
'c:\\Documents and Settings\\mpilgrim\\My Documents' >>> os.path.join(os.path.expanduser("~"), "Python")
'c:\\Documents and Settings\\mpilgrim\\My Documents\\Python'
![]() | 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. |
![]() | 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.) |
![]() | 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. |
![]() | 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. |
![]() | 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")('c:\\music\\ap', 'mahadeva.mp3') >>> (filepath, filename) = os.path.split("c:\\music\\ap\\mahadeva.mp3")
>>> filepath
'c:\\music\\ap' >>> filename
'mahadeva.mp3' >>> (shortname, extension) = os.path.splitext(filename)
>>> shortname 'mahadeva' >>> extension '.mp3'
![]() | 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. |
![]() | 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. |
![]() | The first variable, filepath, receives the value of the first element of the tuple returned from split, the file path. |
![]() | The second variable, filename, receives the value of the second element of the tuple returned from split, the filename. |
![]() | 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\\")['a_time_long_forgotten_con.mp3', 'hellraiser.mp3', 'kairo.mp3', 'long_way_home1.mp3', 'sidewinder.mp3', 'spinning.mp3'] >>> dirname = "c:\\" >>> os.listdir(dirname)
['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))]
['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))]
['cygwin', 'docbook', 'Documents and Settings', 'Incoming', 'Inetpub', 'Music', 'Program Files', 'Python20', 'RECYCLER', 'System Volume Information', 'TEMP', 'WINNT']
![]() | The listdir function takes a pathname and returns a list of the contents of the directory. |
![]() | listdir returns both files and folders, with no indication of which is which. |
![]() | 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. |
![]() | 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.
![]() | |
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
More on modules | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Putting it all together |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Средства объектно-ориентированного программирования > Putting it all together | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
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]
![]() | 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. |
![]() | 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). |
![]() | 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. |
![]() | 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. |
![]() | 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. |
![]() | 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.
The os module | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Summary |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Applicability and definitions | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
GNU Free Documentation License | 1 2 3 4 5 6 7 8 9 10 11 12 | Verbatim copying |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Verbatim copying | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Applicability and definitions | 1 2 3 4 5 6 7 8 9 10 11 12 | Copying in quantity |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Copying in quantity | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Verbatim copying | 1 2 3 4 5 6 7 8 9 10 11 12 | Modifications |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Modifications | << >> | ||||
![]() | В глубь языка Python Для программистов |
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:
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.
Copying in quantity | 1 2 3 4 5 6 7 8 9 10 11 12 | Combining documents |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Combining documents | << >> | ||||
![]() | В глубь языка Python Для программистов |
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."
Modifications | 1 2 3 4 5 6 7 8 9 10 11 12 | Collections of documents |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Collections of documents | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Combining documents | 1 2 3 4 5 6 7 8 9 10 11 12 | Aggregation with independent works |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Aggregation with independent works | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Collections of documents | 1 2 3 4 5 6 7 8 9 10 11 12 | Translation |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Translation | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Aggregation with independent works | 1 2 3 4 5 6 7 8 9 10 11 12 | Termination |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Termination | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Translation | 1 2 3 4 5 6 7 8 9 10 11 12 | Future revisions of this license |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > Future revisions of this license | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Termination | 1 2 3 4 5 6 7 8 9 10 11 12 | How to use this License for your documents |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > GNU Free Documentation License > How to use this License for your documents | << >> | ||||
![]() | В глубь языка Python Для программистов |
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.
Future revisions of this license | 1 2 3 4 5 6 7 8 9 10 11 12 | Python 2.1.1 license |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Python 2.1.1 license | << >> | ||||
![]() | В глубь языка Python Для программистов |
Содержание
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.
How to use this License for your documents | 1 2 | Terms and conditions for accessing or otherwise using Python |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |
Начало > В глубь языка Python > Python 2.1.1 license > Terms and conditions for accessing or otherwise using Python | << | ||||
![]() | В глубь языка Python Для программистов |
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.
Python 2.1.1 license | 1 2 | |
Copyright © 2000, 2001, 2002 Марк Пилгрим Copyright © 2001, 2002 Перевод, Денис Откидач | ![]() | ![]() |