Вглубь языка Python
Примеры лежат тут (120 кб) .
Copyright © 2000, 2001, 2002
Марк ПилгримЗдесь представлена полноценная программа на языке Python. Возможно эта программа не представляет для вас никакого смысла. Не
беспокойтесь, мы проанализируем каждую строку. Но сначала прочитайте ее и
посмотрите, в чем вы смогли разобраться самостоятельно. Пример 1.1. odbchelper.py
def buildConnectionString(params):
"""Создает и возвращает строку соединения из словаря параметров."""
return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
if __name__ == __main__:
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret"}
print buildConnectionString(myParams) Теперь запутите программу и посмотрите,
что произойдет. | В Python IDE под Windows вы можете
запустить программу из меню
File->Run... (Ctrl-R). Вывод программы отображается в интерактивном окне. |
| В Python IDE on Mac OS вы также можете запустить программу из меню
Python->Run window... (Cmd-R), но сначала необходимо выставить одну важную опцию. Откройте
модуль в IDE, откройте меню модуля, нажав на черный треугольник в верхнем
правом углу окна модуля, и убедитесь, что установлена опция
“Run as
__main__”. Эта опция сохраняется вместе с
модулем, так что ее необходимо установить для каждого модуля только один
раз. |
| В UNIX-совместимых система (включая Mac OS X) вы можете запустить
программу из командной строки: python
odbchelper.py |
Пример 1.2. Вывод odbchelper.py server=mpilgrim;uid=sa;database=master;pwd=secret
1.2. Объявление функцийВ языке Python, как и в большинстве других языков программирования,
есть функции, но в нем нет отдельных заголовочных файлов, как в C++, или
разделов интерфейс/реализация, как в языке Pascal. Если вам нужна функция
— просто определите ее. Пример 1.3. Определение функции buildConnectionString
def buildConnectionString(params): Здесь необходимо сделать несколько замечаний. Во-первых, определение
функции начинается с ключевого слова def, после которого следуют имя функции
и, в скобках, аргументы. В данном примере функция имеет только один аргумент,
если же функция должна воспринимать несколько аргументов, они перечисляются
через запятую. Во-вторых, вы не определяете тип возвращаемого значения. В языке
Python никогда не указывается не только тип возвращаемого значения, но даже
его наличие. На самом деле каждая функция возвращает значение; если функция
выполняет инструкцию return, она возвращает указанное в ней значени, иначе
функция возвращает специальное значение — None. | В языке Visual Basic определение функций (возвращающих значение) начинается с
ключевого слова function, а подпрограмм (не возвращающих
значение) — с sub. В языке Python нет подпрограмм.
То, что называется подпрограммой в других языках, в языке Python является
функцией. Любая функция возвращает значение, даже если это None, и
определение любой функции начинается с def. |
В третьих, не указан тип аргумента params. В языке
Python тип переменных никогда не указывается явно. Python отслеживает тип
объектов во время выполнения программы. | В Java, C++ и других языках со статической типизацией вы должны
указывать тип возвращаемого значения и аргументов функции. В языке Python вы
никогда не указываете тип. Тип переменной определяется, когда вы присваиваете
ей значение. |
Дополнение. Эрудированный читатель прислал мне следуещее объяснение сравнения
Python с другими языками программирования: - Язык со статической типизацией
- Язык, в котором тип (переменной, возвращаемого значения) должен
быть указан, например Java или C.
- Язык с динамической типизацией
- Язык, в котором тип определяется во время выполнения.
VBScript и Python являются языками с динамической типизацией, так как они
определяют тип переменной, когда ей присваивается значение.
- Язык со строгой типизацией
- Язык, в котором тип всегда четко определен, например Java или
Python. Например, когда целое число не можете быть вами использовано в
качестве строки без явного преобразования (мы опишем, как это сделать
ниже).
- Язык со слабой типизацией
- Язык, в котороом типы можно игнорировать. Например в
VBScript вы можете объединить строку '12' с целым числом
3 для получения строки '123', которую
затем будете использовать в качестве целого числа 123, и все
это без явных преобразований.
Таким образом Python является языком со строгой
(тип переменной имеет значение) динамической (не
используется объявление типа) типизацией.
1.3. Документирование функцийВ языке Python вы можете документировать функции, снабжая их
строками документации. Пример 1.4. Определение строки документации для buildConnectionString
def buildConnectionString(params):
"""Создает и возвращает строку соединения из словаря параметров.""" Утроенные кавычки используются для многострочных строковых литералов.
Все, что находится между утроенными кавычками, является одним строковым
значением, включая символы перехода на новую строку и другие символы кавычек.
Вы можете использовать их где угодно, но чаще всего их употребляют для
определения строк документации. | Утроенные кавычки являются удобным способом определения строк, содержащих
одновременно одинарные и двойные кавычки, аналогично qq/.../
в Perl. |
Первая строка в определении функции (то есть первая после двоеточия)
является строкой документации, в которой поясняется, что функция делает.
Python не требует наличия строки документации у функции, но все же ее стоит
всегда определять. Я знаю, вы слышали об этом на каждой лекции по
программированию, но Python дает вам дополнительный стимул: строка
документации доступна во время выполнения программы в качестве атрибута
функции. | Многие Python IDE используют строки документации для контекстной
справки. Например, во время ввода имени функции строка документации может
отображаться в виде всплывающей подсказки. Такая помощь может быть очень
полезной — все зависит от того, насколько хорошо написана строка
документации. |
Дополнительная литература
1.4. Все является объектамиЕсли вы не обратили внимание, я только что заметил, что функции в языке
Python имеют атрибуты, и эти атрибуты доступны во время выполнения
программы. Функция, как и все остальное в языке Python, является объектом. Пример 1.5. Доступ к строке документации функции buildConnectionString >>> import odbchelper
>>> 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__
Создает и возвращает строку соединения из словаря параметров. | В первой строке импортируется odbchelper в качестве модуля. После того,
как модуль проимпортирован, вы можете ссылаться на его функции, классы или
атрибуты. Модули также могут импортировать другие модули для доступа к их
функциональности, и вы можете импортировать модули из IDE. Это очень важная
концепция, и мы поговорим о ней позже. | | Для использования функции из импортированного модуля вы должны указать
имя этого модуля. То есть, вместо buildConnectionString необходимо написать
odbchelper.buildConnectionString. Если вы
использовали классы в Java, то заметили отдаленное сходство с ними. | | Вместо привычного вызова функции мы запрашиваем один из ее атрибутов
— __doc__. |
| import в Python работает аналогично require в Perl.
Проимпортировав в Python один раз модуль с помощью инструкции import вы
можете обращаться к его функциям в виде
module.function;
проимпортировав модуль с помощью инструкции require в Perl, вы можете
обращаться к его функциям в виде
module::function. |
В языке Python все является объектами, и почти все из них имеют
атрибуты и методы.[1] Все функции имеют специальный атрибут, который
содержит строку документации, определенную в исходном коде. Это настолько важно, что я повторю еще раз: в языке Python
все является объектами. Строки являются объектами. Списки являются
объектами. Функции являются объектами. И даже модули, как мы скоро увидим,
являются объектами. Дополнительная литература
1.5. ОтступыВ определении функций нет явных begin и
end, нет фигурных скобок, обозначающих начало и конец тела
функции. Единственным разделителем является двоеточие
(“:”) плюс отсуп кода в теле функции. Пример 1.6. Отступы в определении функции buildConnectionString
def buildConnectionString(params):
"""Создает и возвращает строку соединения из словаря параметров."""
return ";".join(["%s=%s" % (k, v) for k, v in params.items()]) Блоки кода (функции, инструкций if, for и др.) определяются
отступами. Увеличение отступа обозначает начало блока и уменьшение — его
конец. Никакие дополнительные скобки или ключевые слова для этих целей не
используются. Таким образом отступы являются значимыми и должны быть
согласованными. В приведенном примере тело функции (включая строку
документации) набрано с отступом в четыре пробела. Число пробелов в отступе не
обязательно должно быть равно четырем, но должно быть везде одинаковым. Первая
строка, без отступа, находится за пределами тела функции. После некоторого протеста вы смиритесь с ролью отступов в языке Python
и увидите преимущества такого подхода. Программы на языке Python выглядят
схожими, так как отступы являются требованием языка, а не стилем. Поэтому
чужой код воспринимается гораздо легче. | Python использует переход на новую строку для разделения инструкций и
отступы для выделения блоков. В C++ и Java для разделения инструкций
используется точка с запятой, а блоки выделяются фигурными скобками. |
Дополнительная литература
1.6. Тестирование модулейМодули в языке Python также являются объектами и имеют несколько
полезных атрибутов. Вы можете использовать их, например, для
тестирования. Пример 1.7. Прием с if __name__
if __name__ == "__main__": Несколько замечаний, перед тем как мы перейдем к главному. Во-первых,
выражение в инструкции if совсем не обязательно заключать в скобки.
Во-вторых, заголовок инструкции закачивается двоеточием, после которого следует
код, выделенный отступами. | Аналогично C, Python использует == для проверки
равенства и = для присваивания. Но, в отличии от C,
Python не поддерживает присваивание внутри выражения, поэтому исключена
возможность случайного присваивания значения, когда имеется ввиду проверка на
равенство. |
Так что же делает этот прием с if? Модули являются объектами и имеют
атрибут __name__. Значение этого атрибута зависит от того, как используется
модуль. Если вы импортируете модуль, атрибут __name__ равен имени файла без
каталога и расширения. Но если вы запускаете модуль как отдельную программу,
__name__ будет иметь специальное значение — __main__. Пример 1.8. __name__ импортированного модуля >>> import odbchelper
>>> odbchelper.__name__
'odbchelper' Зная об этом, вы можете определить код для тестирования модуля
непосредственно в самом модуле. При непосредственном запуске модуля __name__
будет равен __main__, что приведет к выполнению теста. При импортировании же
модуля __name__ будет иметь другое значение, и тест будет проигнорирован. Такой
подход облегчает разработку и отладку новых модулей перед включением их в
большую программу. | Для того, чтобы данный прием работал в MacPython, необходимо выполнить
дополнительные шаги. Откройте меню опций модуля, нажав на черный треугольник в
правом верхнем углу, и убедитесь, что отмечен пункт
Run as __main__. |
Дополнительная литература
1.7. СловариСделаем небольшое отступление, так как вам необходимо познакомиться со
словарями, кортежами и списками. Если вы знаток Perl, то уже имеете
некоторое представление о словарях и списках, но вам, тем не менее, необходимо
обратить внимание на кортежи. Один из втроенных типов языка Python, словарь, сопоставляет ключам
значения. | Словари в Python аналогичны хешам в Perl. В Perl имена переменных,
хранящих хеши, всегда начинаются с символа %.
Переменные в языке Python могут иметь произвольное имя, интерпретатор сам
определяет их тип. |
| Словари в Python схожи с экземплярами класса
Hashtable в Java. |
| Словари Python схожи с экземплярами объекта
Scripting.Dictionary в Visual Basic. |
Пример 1.9. Определени словарей >>> d = {"server":"mpilgrim", "database":"master"}
>>> d
{'server': 'mpilgrim', 'database': 'master'}
>>> d["server"]
'mpilgrim'
>>> d["database"]
'master'
>>> d["mpilgrim"]
Traceback (innermost last):
File "<interactive input>", line 1, in ?
KeyError: mpilgrim | Сначала мы создаем словарь с двумя записями и присваиваем его переменной
d. Каждая запись является парой ключ-значение, весь набор
записей перечисляется в фигурных скобках. | | "server" является ключом, а ассоциированное с ним
значение, d["server"], равно
"mpilgrim". | | "database" является ключом, а ассоциированное с ним
значение, d["database"], равно
"master". | | Вы можете определить значение по ключу, но не можете определить ключи по
значению. Так, выражение d["server"] дает
"mpilgrim", но d["mpilgrim"] генерирует
исключение, так как словарь не содержит записи с ключом
"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.8. СпискиСписок являются одним из самых используемых типов данных в языке
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' | Сначала мы определяем список из пяти элементов. Обратите внимание, что
исходный порядок элементов сохраняется. Это не случайно, список является
упорядоченным множеством элементов, перечисленных в квадратных скобках. | | Списки могут быть использованы в качестве массивов. Отсчет элементов
всегда ведется от нуля, так что первый элемент непустого списка —
li[0]. | | Последний элемент списка из пяти элементо —
li[4], так как отсчет ведется от нуля. |
Пример 1.16. Срез >>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[1:3]
['b', 'mpilgrim']
>>> li[1:-1]
['b', 'mpilgrim', 'z']
>>> li[0:3]
['a', 'b', 'mpilgrim'] | Указав через двоеточие два индекса, вы можете получить подмножество
элементов списка, называемое “срезом”. Получаемое значение
является новым списком, содержащим все элементы исходного списка в том же
порядке, начиная с первого индекса (здесь li[1]) до, но не
включая, второго индекса (здесь li[3]). | | В операции среза также можно использовать отрицательные идексы. Если это
поможет, считайте первый индекс соответствующим первому элементу, который вам
нужен, и второй — первому элементу, который не нужен. Получаемое
значение — все, что между ними находится. | | Нумерация элементов начинается с нуля, так что li[0:3]
дает первые три элемента исходного списка. |
Пример 1.17. Сокращения в записи среза >>> li
['a', 'b', 'mpilgrim', 'z', 'example']
>>> li[:3]
['a', 'b', 'mpilgrim']
>>> li[3:]
['z', 'example']
>>> li[:]
['a', 'b', 'mpilgrim', 'z', 'example'] | Если первый индекс среза равен нулю, то его можно опустить. Аналогично
можно опустить второй индекс, если он равен длине списка. То есть
li[:3] дает такой же результат, как и
li[0:3] в предыдущем примере. | | Обратите внимание на симметрию. Для данного списка из пяти элементов
li[:3] дает первые три элемента и li[3:]
— последние два. В самом деле, li[:n] всегда дает
первые n элементов, а li[n:] — все
остальное. | | Если опущены оба индекса, будут включены все элемента исходного списка.
Но это не тот же список, это новый список с теми же элементами. Таким образом,
li[:] позволяет создать копию списка. |
Пример 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'] | Метод append добавляет один элемент в конец
списка. | | Метод insert вставляет один элемент в список.
Целочисленный аргумент является индексом первого элемента, позиция которого
изменится. Обратите внимание, что элементы списка могут быть не уникальными
— после этой операции в списке содержится два элемента со значением
"new", li[2] и
li[6]. | | Метод extend добавляет в конец элементы другого списка. В данном
случае второй список содержит два элемента. |
Пример 1.19. Поиск в списке >>> 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 | Метод index находит первое вхождение элемента в
списке и возвращает его индекс. | | index позволяет найти только
первое вхождение элемента. В нашем списке строка
"new" присутствует дважды (li[2] и
li[6]), но метод index вернет индекс
только первого — 2. | | Если указанный элемент в списке не найден, генерируется исключение.
Такое поведение заметно отличается от поведения аналогичных средств в других
языках, возвращающих какой-либо некорректный индекс. Генерация исключения
более удобна, так как работа программы останавливается в месте возникновения
ошибки, а не в момент использования некорректного индекса. | | Для проверки наличия элемента в списке используйте оператор
in, возвращающий 1, если значение найдено, и 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'] | Метод remove удаляет из списка первый элемент с
указанным значением. | | remove удаляет только один
элемент. В данном случае строка "new" присутствует в списке
дважды, но li.remove("new") удалит только первую. | | Если элемент с указанным значением в списке не найден,
remove, как и index, генерирует
исключение. | | Метод pop выполняет сразу два действия: удаляет
последний элемент из списка и возвращает его. Этим он отличается от
li[-1], возвращающего последний элемент, но не изменяющего
список, и li.remove(value),
изменяющего список, но не возвращающего элемент. |
Пример 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] | С помощью оператора + можно “склеивать”
списки. list =
list +
otherlist эквивалентно
list.extend(otherlist),
но при этом создается новый список, в то время как extend изменяет
существующий. | | Python поддерживает операцию +=. li += ['two'] полностью эквивалентно li.extend(['two']). Операция += работает для списков, строк, чисел и может быть переопределена для классов (более подробно о классах читайте в главе 3). | | Оператор * размножает элементы списка. li =
[1, 2] * 3 эквивалентно li = [1, 2] + [1, 2] + [1,
2]. |
Дополнительная литература
1.9. КортежиКортеж — это неизменяемый список. С момента создания кортеж не
может быть изменен никакими способами. Пример 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') | Кортеж определяется так же, как и список, но элементы перечисляются в
круглых скобках вместо квадратных. | | Как и в списках, элементы в кортежах имеют определенный порядок. Точно
так же нумерация элементов начинается с нуля, то есть первым элементом
непустого кортежа всегда является t[0]. | | Как и для списков, отрицательные индексы позволяют вести отсчет элементов
с конца кортежа. | | К кортежам, как и к спискам можно применить операцию среза. Обратите
внимание, что срез списка — новый список, а срез кортежа — новый
кортеж. |
Пример 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 | Вы не можете добавлять элементы в кортеж. У кортежей нет методов
append и extend. | | Вы не можете удалять элементы из кортежа. У кортежей нет методов
remove и pop. | | Вы не можете искать элементы в кортеже с помощью метода
index — у кортежей нет такого метода. | | Однако, вы можете проверить наличие элемента в кортеже с помощью
оператора in. |
Так для чего же нужны кортежи? - Работа с кортежами быстрее, чем со списками. Если вы
определяете постоянный набор значений, и все, что вы хотите с ним когда-либо
делать, это перебирать его элементы, используйте кортеж вместо
списка.
- Помните, я сказал, что в качестве ключей словаря могут выступать
числа, строки и объекты “некоторых других типов”? Кортежи могут
быть ключами словаря, а списки нет.[2]
- Как мы скоро увидим, кортежи используются для получения форматированного представления.
| Кортеж может быть преобразован в список и наоборот. Встроенная функция
tuple воспринимает список в качестве аргумента и возвращает кортеж с теми же
самыми элементами, и функция list воспринимает кортеж в качестве аргумента и
возвращает список. В результате tuple “замораживает” список, а
list его “размораживает”. |
Дополнительная литература
1.10. Определение переменныхПосле того, как вы познакомились со словарями, кортежами и списками,
давайте вернемся к нашему примеру программы —
odbchelper.py. В языке Python есть локальные и глобальные переменные, как и в
большинстве других языков, но нет их явного объявления. Переменные появляются,
когда им присваивают значение, и автоматически уничтожаются при выходе из
области видимости. Пример 1.24. Определение переменной myParams
if __name__ == "__main__":
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret"} Здесь есть несколько интересных моментов. Во-первых, обратите внимание
на отступы. Инструкция if является составной и ее тело должно вводиться с
отступом, как и тело функции. Во-вторых, одна инструкция присваивания переменной записана в несколько
строк с использованием обратного слэша (“\”) в
качестве признака наличия продолжения. | Если инструкция разбита на несколько строк с использованием признака
наличия продолжения (“\”), строки продолжения
могут иметь произвольный отступ. В этом случае строгие правила использования
отступов не действуют. Если ваш Python IDE автоматически выставляет
отступы для строк продолжения, следует, по-видимому, принять их в качестве
умолчания, если у вас нет весомой причины от этого отказаться. |
| Строго говоря, выражения в круглых, квадратных или фигурных скобках
(например, определения словарей) могут быть
разбиты на несколько строк и без использования явного признака наличия
продолжения (“\”). Я предпочитаю использовать
обратный слэш даже если в нем нет необходимости, так как, на мой взгляд, это
облегчает чтение кода. Но это уже дело вкуса. |
В третьих, вы нигде не объявляли переменную myParams,
а просто присвоили ей значение. Это соответствует поведению VBScript без
опции option explicit. К счастью, в отличии от VBScript,
Python не позволит ссылаться на переменную, которой нигде не было присвоено
значение — при попытке это сделать будет сгенерировано исключение. Пример 1.25. Использование несуществующей переменной >>> x
Traceback (innermost last):
File "<interactive input>", line 1, in ?
NameError: There is no variable named 'x'
>>> x = 1
>>> x
1 В один прекраснй день вы будете благодарны языку Python за такое
поведение. Дополнительная литература
1.11. Присваивание сразу нескольких значенийОдна из приятных возможностей языка Python — использование
последовательностей для односременного присваивания нескольких значений. Пример 1.26. Присваивание сразу нескольких значений >>> v = ('a', 'b', 'e')
>>> (x, y, z) = v
>>> x
'a'
>>> y
'b'
>>> z
'e' | v — кортеж из трех элементов и (x, y,
z) — кортеж из трех элементов. Присваивание одного другому
приводит к присваиванию каждого значения из v
соответствующей переменной. |
Использовать эту возможность можно по-разному. У меня часто возникает
необходимость присвоить переменным диапазон значений. В языке 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 | Встроенная функция range возвращает список целых чисел. В простейшем
случае она воспринимает в качестве аргумента верхний предел и возвращает список
целых чисел от нуля до обозначенного придела, но не включая предельное
значение. При желании вы можете указать нижний предел отличный от нуля и шаг
отличный от еденицы. Выполните инструкцию print
range.__doc__ для получения более подробной информации. | | MONDAY, TUESDAY,
WEDNESDAY, THURSDAY,
FRIDAY, SATURDAY и
SUNDAY — переменные, которые мы определяем. Этот
пример взят из модуля calendar, печатающего календари, аналогично программе
cal в UNIX. Этот модуль определяет целочисленные
константы для дней недели. | | Теперь каждая переменная имеет свое значение: MONDAY
равна 0, TUESDAY — единице, и т. д. |
Множественное присваивание также полезно при использовании функций,
возвращающих несколько значений в виде кортежа. Вы просто присваиваете их
отдельным переменным. Так поступают многие стандартные библиотеки языка
Python, включая модуль os, который обсуждается в главе 3. Дополнительная литература
1.12. Форматированное представлениеPython позволяет получить форматированное представление значений в виде
строки. Хотя строка формата может содержать довольно сложные выражения, чаще
всего используется вставка значений в строку с помощью шаблона
%s. | Python использует для строк формата такой же синтаксис, как и функция
sprintf в C. |
Пример 1.28. Введение в форматирование >>> k = "uid"
>>> v = "sa"
>>> "%s=%s" % (k, v)
'uid=sa' | При вычислении всего выражения получается строка. Первый шаблон
%s заменяется значением k, второй —
значением v. Все остальные символы в строке формата (в
данном случае — знак равенства) попадают в результат без
изменений. |
Обратите внимание, что (k, v) является кортежем. Я
уже говорил, что кортежи весьма полезный тип данных. Вы можете подумать, что здесь слишком много работы для простого
объединения строк. И вы будете правы, но форматирование — это не просто
объединение строк. Это даже не просто форматирование. Данная операция также
выполняет приведение типа. Пример 1.29. Форматирование vs. объединение >>> uid = "sa"
>>> pwd = "secret"
>>> print pwd + " is not a good password for " + uid
secret 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 | Оператор + позволяет объединять строки. | | В простейшем случае форматирование дает такой же результат, как и
объединение строк. | | (userCount, ) — кортеж с одним элементом. Да,
его синтаксис выглядит немного странно, но для этого есть весомая причина:
только так можно однозначно определить кортеж. На самом деле, вы всегда можете
ставить запятую после последнего элемента в определении списка, кортежа или
словаря, но запятая необходима, если вы определяете кортеж из одного элемента.
Если бы запятая не была обязательной, интерпретатор не смог бы определить, что
имелось ввиду под выражением (userCount) — кортеж с
одним элементом или просто значение userCount. | | Форматирование также работает и с целыми числами при использовании
шаблона %d вместо %s. | | При попытке сложить строку со значением не строкового типа генерируется
исключение. В отличии от форматирования, простое сложение работает, только
если все значения уже являются строками. |
Дополнительная литература
1.13. Обработка списковОдна из самых мощных особенностей языка 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] | Чтобы понять такую запись, читайте ее справа налево.
li — список, который вы преобразуете. Python
пробегает по всем элементам li, временно присваивает каждый
из них переменной elem, вычисляет значение выражения
elem*2 и добавляет в конец списка,
который вы в результате получаете. | | Обратите внимание, что при использовании расширенной записи списка
исходный список не изменяется. | | Присваивание результата переменной, в которой хранится обрабатываемый
список, безопасно, так как новый список создается в памяти и, когда вычисление
выражения закончено, он присваивается переменной. |
Пример 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')] | Метод keys словаря возвращает список всех ключей. Порядок следования
ключей в этом списке может не соответствовать порядку, в котором определялись
записи словаря (помните, записи в словаре не упорядочены). | | Метод values возвращает список всех значений. Значения в этом списке
идут в таком же порядке, как и ключи в списке, возвращаемом методом keys, то
есть params.values()[n] == params[params.keys()[n]] для всех
n. | | Метод items возвращает список всех записей словаря в виде кортежа
(key,
value). |
Давайте теперь посмотрим, что делает функция buildConnectionString. Она
берет список params.items() и
преобразует его, применяя к каждому элементу строку формата. Полученный список
будет содержать такое же количество элементов, как и
params.items(), но каждый элемент будет
являться строкой с ключом и ассоциированным с ним значением из словаря
params. Пример 1.33. Расширенная запись списка в buildConnectionString, шаг за шагом >>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> params.items()
[('server', 'mpilgrim'), ('uid', 'sa'), ('database', 'master'), ('pwd', 'secret')]
>>> [k for k, v in params.items()]
['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.14. Объединение и разбиение строкВы имеете список строк с парами ключ-значение вида
key=value
и хотите объединить их в одну строку. Для этих целей можно воспользоваться
методом join строковых объектов. Пример 1.34. Объединение строк в buildConnectionString return ";".join(["%s=%s" % (k, v) for k, v in params.items()]) Перед тем, как мы продолжем, сделаю одно интересное замечание. Я
постоянно повторяю, что функции являются объектами, строки являются объектами,
все является объектами. Вы могли подумать, что под строковыми объектами
подразумевались строковые переменные. Но нет, посмотрите
внимательно на этот пример, и вы увидете, что строка ";"
сама по себе является объектом, метод join которого вы вызываете. В любом случае, метод join объединяет элементы списка в одну строку, в
которой элементы оказываются разделены точкой с запятой. Совсем не
обязательно, чтобы в качестве разделителя выступала точка с запятой, не
обязательно, чтобы он был одним символом — разделитель может быть
произвольной строкой. | Метод join может работать только со списками строк, он не делает
никаких преобразований. Если список содержит один или более нестроковых
элементо, будет сгенерировано исключение. |
Пример 1.35. Вывод программы odbchelper.py >>> params = {"server":"mpilgrim", "database":"master", "uid":"sa", "pwd":"secret"}
>>> ["%s=%s" % (k, v) for k, v in params.items()]
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> ";".join(["%s=%s" % (k, v) for k, v in params.items()])
server=mpilgrim;uid=sa;database=master;pwd=secret Именно эту строку возвращает функция buildConnectionString и выводит
программа. Историческая справка. Когда я начал изучать Python, я ожидал, что join будет методом
списка, а строка-разделитель — его аргументом. Многие люди думали точно
также. До появления версии Python 1.6 строки не имели всех этих полезных
методов. Был отдельный модуль string, содержащий функции для работы со
строками. Каждая функция воспринимала строку в качесве первого аргумента.
Функции посчитали достаточно важными, чтобы поместить их непосредственно в
строковые объекты, что имеет смысл для таких функций, как
lower, upper и
split. Однако многие программисты обратили внимание на
новый метод join, приводя аргументы в пользу того, что он должен быть методом
списка или остаться частью старого модуля string (в котором и сейчас
содержится много полезных вещей). Сам я использую новый метод join, но вы
можете увидеть код написанный и по-другому. Если это вас беспокоит, вы можете
использовать вместо него старую функцию
string.join. Вы, наверное, зададите вопрос, есть ли аналогичный метод для разбиения
строки, возвращающий список подстрок? Да, конечно, и называется он
split. Пример 1.36. Разбиение строк >>> li = ['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> s = ";".join(li)
>>> s
'server=mpilgrim;uid=sa;database=master;pwd=secret'
>>> s.split(";")
['server=mpilgrim', 'uid=sa', 'database=master', 'pwd=secret']
>>> s.split(";", 1)
['server=mpilgrim', 'uid=sa;database=master;pwd=secret'] | Метод split выполняет действия, обратные методу join, разбивая строку
на подстроки. Обратите внимание, что сам разделитель
(“;”) не содержится в результате. | | Метод split воспринимает второй необязательный аргумент, указывающий
максимальное число разбиений. (“О-о-о, необязательные
аргументы…” Вы узнаете, как определить функцию с необязательными
аргументами, в следующей главе.) |
| Вызов
anystring.split(delimiter,
1) может быть полезен, если вы хотите найти первое вхождение
подстроки и, затем, работать с фрагментами до этой подстроки (он является
первым элементом возвращаемого списка) и после него (второй элемент
возвращаемого списка). |
Дополнительная литература
1.15. ЗаключениеТеперь программа odbchelper.py и ее вывод приобретают
смысл. Пример 1.37. odbchelper.py
def buildConnectionString(params):
"""Создает и возвращает строку соединения из словаря параметров."""
return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
if __name__ == "__main__":
myParams = {"server":"mpilgrim", \
"database":"master", \
"uid":"sa", \
"pwd":"secret"}
print buildConnectionString(myParams) Пример 1.38. Вывод программы odbchelper.py server=mpilgrim;uid=sa;database=master;pwd=secret Перед тем, как погрузиться в чтение второй главы, убедитесь, что вы
овладели следующими навыками:
|
Алексей | А пример какой-нить программы написать слабо? 2005-12-11 17:42:06 | Яковлев Се� | Да как-то питон за последний год вообще выпал из обоймы моих интересов.
И вряд ли туда вернется вообще ... 2005-12-11 21:15:45 | |
|