Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org
 

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

Примеры лежат тут (120 кб) . Copyright © 2000, 2001, 2002 Марк Пилгрим

Оригинал этой книги опубликовон по адресу http://diveintopython.org/, 1.1. В глубь

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

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

Пример 1.1. odbchelper.py


 def buildConnectionString(params):
     """Создает и возвращает строку соединения из словаря параметров."""
     return ";".join(["%s=%s" % (k, v) for k, v in params.items()])
 
 if __name__ == __main__:
     myParams = {"server":"mpilgrim", \
                 "database":"master", \
                 "uid":"sa", \
                 "pwd":"secret"}
 print buildConnectionString(myParams)

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

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

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

server=mpilgrim;uid=sa;database=master;pwd=secret
1.2. Объявление функций

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

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


 def buildConnectionString(params):

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Footnotes

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

1.5. Отступы

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

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


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

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

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

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

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

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

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

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


 if __name__ == "__main__":

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

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

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

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

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

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

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

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

1.7. Словари

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Работа с кортежами быстрее, чем со списками. Если вы определяете постоянный набор значений, и все, что вы хотите с ним когда-либо делать, это перебирать его элементы, используйте кортеж вместо списка.
  • Помните, я сказал, что в качестве ключей словаря могут выступать числа, строки и объекты “некоторых других типов”? Кортежи могут быть ключами словаря, а списки нет.[2]
  • Как мы скоро увидим, кортежи используются для получения форматированного представления.
Замечание
Кортеж может быть преобразован в список и наоборот. Встроенная функция tuple воспринимает список в качестве аргумента и возвращает кортеж с теми же самыми элементами, и функция list воспринимает кортеж в качестве аргумента и возвращает список. В результате tuple “замораживает” список, а list его “размораживает”.

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

Footnotes

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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