len(d2) # Число элементов словаря
3
>>> d2.has_key('ham') # Проверка на вхождение
True
>>> 'ham' in d2 # Проверка на вхождение, альтернативный способ
True
>>> d2.keys() # Создает новый список ключей
['eggs', 'ham', 'spam']
Обратите внимание на третье выражение в этом листинге. Как уже упоминалось ранее, оператор проверки на вхождение in может исполь
зоваться для работы со строками и списками, но точно так же он мо
жет использоваться и для работы со словарями. Он проверяет наличие указанного ключа в словаре точно так же, как и метод has_key строкой выше. Это возможно благодаря тому, что словари определяют итера+
торы
, которые обеспечивают пошаговый обход списков ключей. Су
ществуют и другие типы, которые поддерживают итераторы, отвечаю
щие обычному использованию типа; например, файлы имеют итерато
ры, которые позволяют выполнять построчное чтение данных. Итера
торы будут рассматриваться в главах 17 и 24.
Далее в этой главе и в книге вы узнаете о двух альтернативных спосо
бах создания словарей, которые демонстрируются в конце табл. 8.2:
можно передать функции dict (которая фактически является конст
руктором типа) упакованные списки ключей/значений в виде корте
жей или значения в виде именованных аргументов. Передача значе
ний в виде именованных аргументов будет рассматриваться в главе 16.
Использование функции zip, которая представляет собой способ кон
струирования словарей из списков ключей и значений, будет рассмат
риваться в главе 13. Если заранее невозможно предугадать, какие ключи и значения будут получаться во время работы программы, вы всегда сможете создавать их динамически в виде списков и затем упа
ковывать функцией zip.
Изменение словарей
Давайте продолжим работу в интерактивном сеансе. Словари, как и списки, относятся к категории изменяемых объектов, поэтому их можно изменять, увеличивать, уменьшать непосредственно, не созда
вая новые словари: чтобы изменить или создать новую запись в слова
ре, достаточно выполнить операцию присваивания по ключу. Здесь также применима инструкция del – она удаляет значение, связанное с ключом, который играет роль индекса. Обратите также внимание на
Словари в действии
229
наличие вложенного списка в следующем примере (значение для клю
ча 'ham'). Все типыколлекции в языке Python могут вкладываться друг в друга в произвольном порядке:
>>> d2['ham'] = ['grill', 'bake', 'fry'] # Изменение элемента
>>> d2
{'eggs': 3, 'spam': 2, 'ham': ['grill', 'bake', 'fry']}
>>> del d2['eggs'] # Удаление элемента
>>> d2
{'spam': 2, 'ham': ['grill', 'bake', 'fry']}
>>> d2['brunch'] = 'Bacon' # Добавление нового элемента
>>> d2
{'brunch': 'Bacon', 'spam': 2, 'ham': ['grill', 'bake', 'fry']}
Как и в случае со списками, в словаре операция присваивания по суще
ствующему индексу приводит к изменению ассоциированного с ним значения. Однако в отличие от списков, словари допускают выполне
ние присваивания по новому ключу (который ранее отсутствовал), в ре
зультате создается новый элемент словаря, как показано в предыду
щем примере для ключа 'brunch'. Этот прием не может применяться к спискам, потому что в этом случае интерпретатор обнаруживает вы
ход за пределы списка и генерирует сообщение об ошибке. Чтобы уве
личить размер списка, необходимо использовать такие средства спи
сков, как метод append или присваивание срезу.
Дополнительные методы словарей
Методы словарей обеспечивают выполнение различных операций. На
пример, методы словарей values и items возвращают список значений элементов словаря и кортежи пар (key, value), соответственно:
>>> d2 = {'spam': 2, 'ham': 1, 'eggs': 3}
>>> d2.values()
[3, 1, 2]
>>> d2.items()
[('eggs', 3), ('ham', 1), ('spam', 2)]
Такие списки удобно использовать в циклах, когда необходимо выпол
нить обход элементов словаря. Попытка извлечения несуществующе
го элемента словаря обычно приводит к появлению ошибки, однако ме
тод get в таких случаях возвращает значение по умолчанию (None или указанное значение). С помощью этого метода легко можно реализо
вать получение значений по умолчанию и избежать появления ошибки обращения к несуществующему ключу:
>>> d2.get('spam') # Ключ присутствует в словаре
2
>>> d2.get('toast') # Ключ отсутствует в словаре
None
>>> d2.get('toast', 88)
88
230Глава 8.
Списки и словариМетод update реализует своего рода операцию конкатенации для слова
рей. Он объединяет ключи и значения одного словаря с ключами и значениями другого, просто перезаписывая значения с одинаковы
ми ключами:
>>>
d2{'eggs': 3, 'ham': 1, 'spam': 2}
>>>
d3 = {'toast':4, 'muffin':5}>>>
d2.update(d3)>>>
d2{'toast': 4, 'muffin': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
Наконец, метод pop удаляет ключ из словаря и возвращает его значе
ние. Он напоминает метод pop списков, только вместо необязательного индекса элемента принимает ключ:
# удаление элементов словаря по ключу
>>>
d2{'toast': 4, 'muffin': 5, 'eggs': 3, 'ham': 1, 'spam': 2}
>>>
d2.pop('muffin')5
>>>
d2.pop('toast') # Удаляет и возвращает значение заданного ключа4
>>>
d2{'eggs': 3, 'ham': 1, 'spam': 2}
# удаление элементов списка по позиции
>>>
L = ['aa', 'bb', 'cc', 'dd']>>>
L.pop() # Удаляет и возвращает последний элемент списка'dd'
>>>
L['aa', 'bb', 'cc']
>>>
L.pop(1) # Удаляет и возвращает элемент из заданной позиции'bb'
>>>
L['aa', 'cc']
Кроме того, словари имеют метод copy, который мы рассмотрим в сле
дующей главе, как один из способов избежать побочных эффектов,
связанных с наличием нескольких ссылок на один и тот же словарь.
В действительности словари обладают гораздо большим числом мето
дов, чем перечислено в табл. 8.2. Чтобы получить полный список, об
ращайтесь к руководствам по языку Python.
Таблица языковДавайте рассмотрим более жизненный пример словаря. В следующем примере создается таблица, которая отображает названия языков про
граммирования (ключи) на имена их создателей (значения). С помо
щью этой таблицы можно по названию языка определить имя его соз
дателя:
Словари в действии
231>>>
table = {'Python': 'Guido van Rossum',...
'Perl': 'Larry Wall',...
'Tcl': 'John Ousterhout' }>>>
language = 'Python'>>>
creator = table[language]>>>
creator'Guido van Rossum'
>>>
for lang in table.keys():...
print lang, '\t', table[lang]Tcl John Ousterhout
Python Guido van Rossum
Perl Larry Wall
В последней команде использован оператор цикла for, который мы еще подробно не рассматривали. Для тех, кто не знаком с циклами for, за
мечу, что приведенная команда просто выполняет обход всех ключей в
таблице и выводит список ключей и их значений, разделенных симво
лом табуляции. Подробно о циклах for будет рассказываться в главе 13.
Так как словари не являются последовательностями, их нельзя напря
мую использовать в операторе цикла for, как это допускают строки и списки. Но, если необходимо выполнить обход элементов словаря, это легко сделать с помощью метода keys, возвращающего список всех клю
чей словаря, который может использоваться для выполнения итераций в цикле for. В случае необходимости внутри цикла можно получать зна
чение элемента по его ключу, как это реализовано в данном примере.
Кроме того, Python в действительности позволяет выполнять обход списка ключей словаря и без вызова метода keys в операторе цикла for.
Для любого словаря D цикл можно оформить как for key in D:, что рав
носильно полной форме записи for key in D.keys():. Это всего лишь еще одна разновидность итераторов, упоминавшихся ранее, которая позво
ляет использовать оператор проверки вхождения in со словарями (под
робнее об итераторах далее в книге).
Замечания по использованию словарейСловари окажутся достаточно просты в использовании, когда вы ос
воите работу с ними, но я хочу привести несколько соображений, кото
рые вам следует твердо знать:
•Операции над последовательностями неприменимы к словарямСловари – это отображения, а не последовательности. Вследствие того, что словари не предусматривают никакого упорядочения эле
ментов, такие операции, как конкатенация (упорядоченное объеди
нение) и извлечение среза (извлечение непрерывного блока элемен
тов), просто неприменимы. В действительности, когда в программ
ном коде во время выполнения производится попытка сделать не
что подобное, интерпретатор выдает сообщение об ошибке.
232
Глава 8. Списки и словари
•
Присваивание по несуществующему индексу приводит к созданию
нового элемента
. Ключи можно создавать при определении слова
ря в виде литерала (в этом случае они встраиваются непосредствен
но в литерал) или при присваивании значения новому ключу суще
ствующего объекта словаря. Результат получается тот же самый.
•
Ключи не обязательно должны быть строками
. В наших примерах в качестве ключей использовались строки, но могут использоваться любые другие неизменяемые объекты (то есть не списки). Напри
мер, в качестве ключей допустимо использовать целые числа, что превращает словарь в подобие списка (как минимум, в смысле ин
дексирования). В качестве ключей можно также использовать кор
тежи, что позволяет создавать составные ключи. Экземпляры клас
сов (обсуждаются в четвертой части книги) также могут играть роль ключей при условии, что они поддерживают определенные ме
тоды, которые сообщат интерпретатору, что он имеет дело с неизме
няемым объектом, в противном случае они будут бесполезны, если рассматривать их как фиксированные ключи.
Использование словарей для имитации гибких списков
Последнее замечание в предыдущем списке имеет настолько важное значение, что имеет смысл продемонстрировать его применение на не
скольких примерах. Списки не допускают возможность присваивания по индексам, находящимся за пределами списков:
>>> L = []
>>> L[99] = 'spam'
Traceback (most recent call last):
File "", line 1, in ?
IndexError: list assignment index out of range
(Перевод: Диагностика (самого последнего вызова):
Файл "", строка 1, позиция ?
IndexError: присваивание по индексу за пределами списка)
Можно, конечно, с помощью операции повторения создать список дос
таточно большой длины (например, [0]*100), но можно создать нечто,
что напоминает словарь, который не требует такого выделения про
странства. При использовании целочисленных ключей словари могут имитировать списки, которые увеличиваются при выполнении опера
ции присваивания по смещению:
>>> D = {}
>>> D[99] = 'spam'
>>> D[99]
'spam'
>>> D
{99: 'spam'}
Результат выглядит так, как если бы D был списком из 100 элементов,
но на самом деле – это словарь с единственным элементом – значением
Словари в действии
233
ключа 99 является строка 'spam'. В такой структуре можно обращаться по смещениям, как и в списке, но вам не требуется выделять простран
ство для всех позиций, которые могут когдалибо потребоваться при выполнении программы. При использовании подобным образом сло
вари представляют собой более гибкие эквиваленты списков.
Использование словарей для структур разреженных данных
Похожим образом словари могут использоваться для реализации структур разреженных данных, таких как многомерные массивы, где всего несколько элементов имеют определенные значения:
>>> Matrix = {}
>>> Matrix[(2, 3, 4)] = 88
>>> Matrix[(7, 8, 9)] = 99
>>>
>>> X = 2; Y = 3; Z = 4 # ; отделяет инструкции
>>> Matrix[(X, Y, Z)]
88
>>> Matrix
{(2, 3, 4): 88, (7, 8, 9): 99}
Здесь словарь использован для представления трехмерного массива,
в котором только два элемента, (2,3,4) и (7,8,9), имеют определенные значения. Ключами словаря являются кортежи, определяющие коор
динаты непустых элементов. Благодаря этому вместо трехмерной мат
рицы, объемной и по большей части пустой, оказалось достаточно ис
пользовать словарь из двух элементов. В такой ситуации попытка дос
тупа к пустым элементам будет приводить к возбуждению исключе
ния, так как эти элементы физически отсутствуют:
>>> Matrix[(2,3,6)]
Traceback (most recent call last):
File "", line 1, in ?
KeyError: (2, 3, 6)
(Перевод: Диагностика (самого последнего вызова):
Файл "", строка 1, позиция ?
KeyError: (2, 3, 6))
Как избежать появления ошибок обращения
к несуществующему ключу
Ошибки обращения к несуществующему ключу являются обычными при работе с разреженными матрицами, но едва ли ктото захочет, что
бы они приводили к преждевременному завершению программы. Суще
ствует, по крайней мере, три способа получить значение по умолчанию вместо возбуждения исключения – можно предварительно проверить наличие ключа с помощью условного оператора if, воспользоваться конструкцией try, чтобы перехватить и явно обработать исключение,
или просто использовать показанный ранее метод словаря get, способ
ный возвращать значение по умолчанию для несуществующих ключей:
234
Глава 8. Списки и словари
>>> if Matrix.has_key((2,3,6)): # Проверить наличие ключа перед обращением
... print Matrix[(2,3,6)]
... else:
... print 0
0
>>> try:
... print Matrix[(2,3,6)] # Попытаться обратиться по индексу
... except KeyError: # Перехватить исключение и обработать
... print 0
0
>>> Matrix.get((2,3,4), 0) # Существует; извлекается и возвращается
88
>>> Matrix.get((2,3,6), 0) # Отсутствует; используется аргумент default
0
Способ, основанный на применении метода get, является самым про
стым из приведенных, если оценивать объем программного кода, – ин
струкции if и try подробно будут рассматриваться далее в этой книге.
Использование словарей в качестве «записей»
Как видите, словари в языке Python способны играть множество ро
лей. Вообще говоря, они способны заменить реализацию алгоритмов поиска (потому что операция индексирования по ключу уже является операцией поиска), могут представлять самые разные типы структу
рированной информации. Например, словари представляют один из многих способов описания свойств элементов в программах, то есть они могут играть ту же роль, какую играют «структуры» и «записи»
в других языках программирования.
В следующем примере выполняется заполнение словаря путем при
сваивания значений новым ключам в виде нескольких инструкций:
>>> rec = {}
>>> rec['name'] = 'mel'
>>> rec['age'] = 45
>>> rec['job'] = 'trainer/writer'
>>>
>>> print rec['name']
mel
Встроенные типы языка Python позволяют легко представлять струк
турированную информацию; это особенно заметно, когда появляются уровни вложенности. В следующем примере снова используется сло
варь для хранения свойств объекта, но на этот раз заполнение произво
дится в единственной инструкции (вместо того чтобы выполнять при
сваивание каждому ключу в отдельности), причем здесь присутствуют вложенные список и словарь, чтобы обеспечить представление струк
турированных свойств объекта:
>>> mel = {'name': 'Mark',
Словари в действии
235
... 'jobs': ['trainer', 'writer'],
... 'web': 'www.rmi.net/lutz',
... 'home': {'state': 'CO', 'zip':80513}}
Чтобы извлечь компоненты вложенных объектов, достаточно просто объединить в цепочку операции индексирования:
>>> mel['name']
'Mark'
>>> mel['jobs']
['trainer', 'writer']
>>> mel['jobs'][1]
'writer'
>>> mel['home']['zip']
80513
Другие способы создания словарей
Наконец, обратите внимание, что благодаря практической ценности словарей с течением времени появились и другие способы их создания.
В Python 2.3 и более поздних версиях, например, последние два вызова конструктора dict в следующем ниже примере имеют тот же эффект,
что и литералы и форма присваивания по отдельным ключам в приме
ре выше:
{'name': 'mel', 'age': 45} # Традиционное литеральное выражение
D = {} # Динамическое присваивание по ключам
D['name'] = 'mel'
D['age'] = 45
dict(name='mel', age=45) # Форма именованных аргументов
dict([('name', 'mel'), ('age', 45)]) # Кортежи ключ/значение
Все четыре варианта создают один и тот же словарь, содержащий два элемента:
•
Первый вариант удобен, если содержимое всего словаря известно заранее.
•
Второй вариант удобно использовать, когда необходимо динамиче
ски создавать словарь по одному полю за раз.
•
Третий вариант с использованием именованных аргументов даже компактнее, чем литералы, но он требует, чтобы все ключи были строками.
•
Последний вариант удобен, когда ключи и значения во время выпол
нения программы необходимо хранить в виде последовательностей.
Как было показано в конце табл. 8.2, последний вариант также часто используется в соединении с функцией zip, которая позволяет объеди
нить отдельные списки ключей и значений, создаваемые динамически во время выполнения (например, при извлечении данных из столбцов в файле).
236
Глава 8. Списки и словари
Если значения всех ключей словаря остаются все время одними и теми же, существует специализированная форма инициализации словаря,
при использовании которой достаточно просто передать список клю
чей и начальное значение (по умолчанию используется значение None):
>>> dict.fromkeys(['a', 'b'], 0)
{'a': 0, 'b': 0}
В настоящее время вы вполне можете обойтись простыми литералами и операцией присваивания по ключам, но вы наверняка найдете при
менение всем упомянутым вариантам создания словарей, как только приступите к созданию настоящих, гибких и динамических программ на языке Python.
Придется держать в уме: интерфейсы словарей
Помимо удобного способа хранения информации по ключам непо
средственно в программе некоторые расширения для Python так
же предоставляют интерфейсы, которые выглядят и действуют как словари. Например, обращение к индексированным файлам данных в формате DBM во многом напоминает обращение к слова
рю, который сначала требуется открыть. Строки сохраняются и извлекаются с помощью операции индексирования по ключу:
import anydbm file = anydbm.open("filename") # Ссылка на файл
file['key'] = 'data' # Сохранение данных по ключу
data = file['key'] # Извлечение данных по ключу
Позднее будет показано, как таким же способом можно сохранять целые объекты Python, достаточно лишь заменить имя модуля anydbm на shelve (shelves (хранилища) – это базы данных с дос
тупом к информации по ключу, предназначенные для постоян
ного хранения объектов Python). Для работы в Интернете под
держка CGIсценариев, предусмотренная в языке Python, также обеспечивает интерфейс, напоминающий словарь. Вызов метода cgi.FieldStorage возвращает объект, по своим характеристикам напоминающий словарь, – с одной записью для каждого поля ввода, находящегося на клиентской вебстранице:
import cgi form = cgi.FieldStorage( ) # Parse form data if form.has_key('name'):
showReply('Hello, ' + form['name'].value)
Все эти объекты (и словари в том числе) являются примерами отображений. Как только вы овладеете словарными интерфейса
ми, вы обнаружите, что они имеют отношение ко множеству встроенных инструментов языка Python.
В заключение
237В заключениеВ этой главе мы исследовали списки и словари – два, пожалуй, наибо
лее часто используемых, гибких и мощных типа коллекций, которые можно увидеть в программах на языке Python. Мы узнали, что списки представляют собой упорядоченные по позициям коллекции объектов произвольных типов и что они могут
иметь неограниченное число уровней вложенности, могут увеличиваться и уменьшаться в размерах по мере необходимости и многое другое. Словари представляют похо
жий тип данных, но в них элементы сохраняются по ключам, а не по позициям; словари не обеспечивают какойлибо надежный способ под
держки упорядочения элементов слева направо. И списки, и словари относятся к категории изменяемых объектов и поэтому поддерживают различные операции непосредственного их изменения, недоступные для строк, например, списки можно увеличивать в размерах с помо
щью метода append, а словари – за счет выполнения операции присваи
вания по новому ключу.
В следующей главе мы закончим подробное исследование базовых ти
пов объектов, рассмотрев кортежи и файлы. После этого мы перейдем к инструкциям, которые реализуют логику обработки этих объектов,
что позволит нам еще больше приблизиться к написанию полноцен
ных программ. Но прежде чем заняться этими темами, ответьте на контрольные вопросы главы.
Закрепление пройденногоКонтрольные вопросы1. Назовите два способа создания списка, содержащего пять целочис
ленных значений, равных нулю.
2. Назовите два способа создания словаря с двумя ключами 'a' и 'b',
каждый из которых ассоциирован со значением 0.
3. Назовите четыре операции, которые изменяют непосредственно объект списка.
4. Назовите четыре операции, которые изменяют непосредственно объект словаря.
Ответы1. Литеральное выражение, такое как [0, 0, 0, 0, 0], и операция по
вторения, такая как [0]*5, создадут список с пятью элементами, со
держащими нули. Формально можно было бы построить список с помощью цикла, начиная с пустого списка, к которому добавляет
ся значение 0 на каждой итерации: L.append(0). Генератор списков
([0 for i in range(5)]) сделал бы то же самое, но в этом случае при
шлось бы выполнить лишнюю работу.
238
Глава 8. Списки и словари
2. Литеральное выражение, например, {'a': 0, 'b': 0}, или последова
тельность операций присваивания, таких как D = []; D['a'] = 0,
D['b']
= 0, создадут требуемый словарь. Можно также использовать более новый и более простой способ с использованием именованных аргументов dict(a=0, b=0) или более гибкий вариант с указанием по
следовательностей пар ключ/значение dict([('a', 0), ('b', 0)]).
Или, поскольку все ключи известны заранее и не будут изменяться в дальнейшем, можно использовать специализированную форму dict.fromkeys(['a',
'b'], 0).
3. Методы append и extend увеличивают размер самого списка, методы sort и reverse упорядочивают и изменяют порядок следования эле
ментов списка на обратный, метод insert вставляет элемент в ука
занную позицию, методы remove и pop удаляют элементы списка по значениям и по смещению, инструкция del удаляет элемент списка или срез, а операции присваивания по индексу или срезу замещают элемент или целый сегмент списка. Для правильного ответа на во
прос выберите четыре любых метода из указанных.
4. Словари прежде всего изменяются с помощью инструкции присваи
вания по новому или по существующему ключу, что приводит к соз
данию или изменению записи в таблице. Кроме того, инструкция del удаляет ключ, метод update вклеивает один словарь в другой,
а вызов D.pop(key) удаляет ключ и возвращает его значение. Словари имеют и другие, более необычные методы изменения, которые не были перечислены в этой главе, такие как setdefault. За дополни
тельной информацией обращайтесь к справочным руководствам.
9Кортежи, файлы и все остальноеЭта глава завершает наше детальное исследование базовых типов объек
тов языка Python рассмотрением
кортежей (коллекций объектов, кото
рые не могут изменяться) и
файлов (интерфейсов к внешним файлам в вашем компьютере). Как вы узнаете, кортежи – это относительно про
стые объекты, поддерживающие операции, которые по большей части вам уже знакомы по строкам и спискам. Объектыфайлы – это широко используемые многофункциональные инструменты для работы с фай
лами – краткий обзор файлов, что приводится здесь, будет дополнен примерами их использования в последующих главах этой книги.
Кроме того, эта глава завершает данную часть книги рассмотрением свойств, общих для объектов всех типов, с которыми мы познакоми
лись, – понятия равенства, сравнения, копий объекта и т. д. Мы также кратко познакомимся и с другими типами объектов, присутствующи
ми в арсенале Python, – несмотря на то, что мы рассмотрели все основ
ные встроенные типы, спектр объектов в языке Python гораздо шире,
чем я давал вам основания полагать к этому моменту. В заключение мы закроем эту часть книги
изучением связанных с типами объектов ловушек, в которые часто попадаются программисты, и исследуем не
которые примеры, которые позволят вам поэкспериментировать с ос
военными идеями.
1 1
После детального изучения удобно пользоваться краткими справочника
ми, например, представленными в справочном разделе официального сай
та проекта Python. Особо стоить отметить Мастерскую отцаоснователя Py
thon (Python Workshop by Guido van Rossum). Презентация доступна по ад
ресу:
http://www.python.org/doc/essays/ppt/acm+ws/. –
Примеч. науч. ред.
240
Глава 9. Кортежи, файлы и все остальное
Кортежи
Последний тип коллекций в нашем обзоре – это кортежи. Кортежи представляют собой простые группы объектов. Они работают точно так же, как списки, за исключением того, что не допускают непосредствен
ного изменения (они являются неизменяемыми) и в литеральной фор
ме записываются как последовательность элементов в круглых, а не в квадратных скобках. Кортежи не имеют методов, но они обладают большинством свойств, присущим спискам. Ниже коротко рассматри
ваются их свойства. Кортежи:
Это упорядоченные коллекции объектов произвольных типов
Подобно строкам и спискам кортежи являются коллекциями объ
ектов, упорядоченных по позициям (т. е. они обеспечивают упоря
дочение своего содержимого слева направо). Подобно спискам они могут содержать объекты любого типа.
Обеспечивают доступ к элементам по смещению
Подобно строками и спискам доступ к элементам кортежей осуще
ствляется по смещению (а не по ключу) – они поддерживают все операции, которые основаны на использовании смещения, такие как индексирование и извлечение среза.
Относятся к категории неизменяемых последовательностей
Подобно строкам кортежи являются неизменяемыми объектами –
они не поддерживают никаких операций непосредственного изме
нения, которые применяются к спискам. Подобно строкам и спи
скам кортежи являются последовательностями и поддерживают многие операции над последовательностями.
Имеют фиксированную длину, гетерогенны и поддерживают
произвольное число уровней вложенности
Поскольку кортежи являются неизменяемыми объектами, вы не можете изменить размер кортежа, минуя процедуру создания ко
пии. С другой стороны, кортежи могут хранить другие составные объекты (т. е. списки, словари и другие кортежи), а следовательно,
поддерживают произвольное число уровней вложенности.
Массив ссылок на объекты
Подобно спискам кортежи проще представлять как массивы ссы
лок на объекты – кортежи хранят указатели (ссылки) на другие объекты, а операция индексирования над кортежами выполняется очень быстро.
В табл. 9.1 приводятся наиболее часто используемые операции над кортежами. В программном коде кортежи записываются как последо
вательность объектов (точнее, выражений, которые создают объекты),
разделенных запятыми, заключенная в круглые скобки. Пустые кор
тежи определяются как пара пустых круглых скобок.
Кортежи в действии
241
Таблица 9.1. Литералы кортежей и операции
Кортежи в действии
Как обычно, запустите интерактивный сеанс работы с интерпретато
ром Python, чтобы приступить к исследованию кортежей в действии.
Обратите внимание: в табл. 9.1 отсутствуют методы кортежей (т. е. ме
тод append к кортежам неприменим). Это действительно так, зато кор
тежи поддерживают обычные операции над последовательностями,
которые применяются к строкам и к спискам:
>>> (1, 2) + (3, 4) # Конкатенация
(1, 2, 3, 4)
>>> (1, 2) * 4 # Повторение
(1, 2, 1, 2, 1, 2, 1, 2)
>>> T = (1, 2, 3, 4) # Индексирование, извлечение среза
>>> T[0], T[1:3]
(1, (2, 3))
Особенности синтаксиса определения кортежей:
запятые и круглые скобки
Вторая и четвертая строки в табл. 9.1 заслуживают дополнительных пояснений. Поскольку круглые скобки могут также окружать выра
жения (глава 5), необходимо чтото предпринять, чтобы дать интер
претатору понять, что единственный объект в круглых скобках – это кортеж, а не простое выражение. Если вам действительно необходимо получить кортеж с единственным элементом, нужно просто добавить запятую после этого элемента, перед закрывающей круглой скобкой:
Операция
Интерпретация
()
Пустой кортеж t1 = (0,)
Кортеж из одного элемента (не выражение)
t2 = (0, 'Ni', 1.2, 3)
Кортеж из четырех элементов t2 = 0, 'Ni', 1.2, 3
Еще один кортеж из четырех элементов (тот же самый, что и строкой выше)
t3 = ('abc', ('def', 'ghi'))
Вложенные кортежи t1[i]
t3[i][j]
t1[i:j]
len(t1)
Индекс, индекс индекса, срез, длина t1 + t2
t2 * 3
Конкатенация, повторение for x in t
'spam' in t2
Обход в цикле, проверка вхождения
242Глава 9. Кортежи, файлы и все остальное
>>>
x = (40) # Целое число>>>
x40
>>>
y = (40,) # Кортеж, содержащий целое число>>>
y(40,)
В виде исключения при определении кортежей интерпретатор позво
ляет опускать открывающую и закрывающую круглые скобки, если синтаксически конструкция интерпретируется однозначно. Например,
в четвертой строке таблицы кортеж создается простым перечислением четырех элементов, разделенных запятыми. В контексте операции присваивания интерпретатор распознает, что это кортеж, даже при от
сутствии круглых скобок.
Ктото может посоветовать вам всегда использовать круглые скобки в кортежах, а ктото – посоветовать никогда не использовать их (най
дутся и те, кто не скажет вам, что с кортежами делать!). Единственное место, где круглые скобки являются
обязательными, – при передаче кортежей функциям в виде литералов (где круглые скобки имеют важ
ное значение) и при передаче их инструкции print (где важное значе
ние имеют запятые).
Начинающим программистам можно посоветовать следующее – веро
ятно, легче использовать круглые скобки, чем выяснять ситуации, ко
гда они могут быть опущены. Многие также считают, что круглые скобки повышают удобочитаемость сценариев, делая кортежи в про
граммном коде более заметными, но у вас может быть свое собственное мнение на этот счет.
Преобразования и неизменяемостьХотя синтаксис литералов отличается, операции, выполняемые над кортежами (последние три строки в табл. 9.1), идентичны операциям,
применяемым к строкам и спискам.
Единственное отличие состоит в том, что операции +, * и извлечения среза при применении к корте
жам возвращают новые
кортежи, а также в том, что в отличие от строк, списков и словарей, кортежи не имеют методов. Если, к приме
ру, необходимо отсортировать содержимое кортежа, его сначала сле
дует преобразовать в список, чтобы превратить его в изменяемый объ
ект и получить доступ к методу сортировки:
>>>
T = ('cc', 'aa', 'dd', 'bb')>>>
tmp = list(T) # Создается список из элементов кортежа>>>
tmp.sort() # Сортировка списка>>>
tmp['aa', 'bb', 'cc', 'dd']
>>>
T = tuple(tmp) # Создается кортеж из элементов списка>>>
T('aa', 'bb', 'cc', 'dd')
Кортежи в действии
243
Здесь list и tuple – это встроенные функции, которые используются для преобразования в список и затем обратно в кортеж. В действитель
ности обе функции создают новые объекты, но благодаря им создается эффект преобразования.
Для преобразования кортежей можно также использовать генераторы списков (list comprehensions). Например, ниже из кортежа создается список, причем попутно к каждому элементу прибавляется число 20:
>>> T = (1, 2, 3, 4, 5)
>>> L = [x + 20 for x in T]
>>> L
[21, 22, 23, 24, 25]
Генераторы списков в действительности являются операциями над по
следовательностями – они всегда создают новые списки, но они могут использоваться для обхода содержимого любых объектов последователь
ностей, включая кортежи, строки и другие списки. Как будет показано дальше, они могут применяться даже к программным компонентам, ко
торые физически не являются последовательностями, – к любым объек
там, поддерживающим возможность выполнения итераций, включая файлы, которые автоматически читаются строка за строкой.
Кроме того, следует заметить, что правило неизменяемости кортежа применимо только к самому кортежу, но не к объектам, которые он со
держит. Например, список внутри кортежа может изменяться как обычно:
>>> T = (1, [2, 3], 4)
>>> T[1] = 'spam' # Ошибка: нельзя изменить сам кортеж
TypeError: object doesn't support item assignment
(Перевод: TypeError: объект не поддерживает операцию присваивания элементам)
>>> T[1][0] = 'spam' # Допустимо: вложенный изменяемый объект можно изменить
>>> T
(1, ['spam', 3], 4)
Для большинства программ такой одноуровневой неизменяемости для обычного использования кортежей вполне достаточно. О чем, совер
шенно случайно, и рассказывается в следующем разделе.
Для чего нужны кортежи, когда уже имеются списки?
Похоже, что это самый первый вопрос, который задают начинающие программисты, узнав о кортежах: «Зачем нам нужны кортежи, когда у нас уже имеются списки?». Некоторые из причин корнями уходят в прошлое – создатель языка Python является математиком по образо
ванию, который видел кортежи как простые ассоциации объектов,
а списки – как структуры данных, допускающие изменения в течение всего времени своего существования.
Однако более правильным будет считать, что неизменяемость корте
жей обеспечивает своего рода поддержку целостности, – вы можете
244Глава 9. Кортежи, файлы и все остальное быть уверены, что кортеж не будет изменен посредством другой ссыл
ки из другого места в программе, чего нельзя сказать о списках. Тем самым кортежи играют роль объявлений «констант», присутствую
щих в других языках программирования, несмотря на то, что в языке
Python это понятие связано с объектами, а не с переменными.
Кроме того, существуют ситуации, в которых кортежи можно исполь
зовать, а списки – нет. Например, в качестве ключей словаря (пример с разреженными матрицами в главе 8). Некоторые встроенные опера
ции также могут требовать или предполагать использование корте
жей, а не списков. Следует запомнить, что списки должны выбирать
ся, когда требуются упорядоченные коллекции, которые может потре
боваться изменить. Кортежи могут использоваться и в остальных слу
чаях, когда необходимы фиксированные ассоциации объектов.
ФайлыВозможно, вы уже знакомы с понятием файла – так называются об
ласти постоянной памяти в вашем компьютере, которыми управляет операционная система. Последний основной встроенный тип объек
тов, который мы исследуем в нашем обзоре, обеспечивает возможность доступа к этим файлам из программ на языке Python.
Проще говоря, встроенная функция open создает объект файла, кото
рый обеспечивает связь с файлом, размещенным в компьютере. После вызова функции open можно выполнять операции чтения и записи во внешний файл, используя методы полученного объекта. Встроенное имя file является синонимом имени open, поэтому файлы могут откры
ваться обращением к функции как по имени open, так и по имени file.
Обычно для выполнения операции открытия файла предпочтительнее использовать функцию open, тогда как имя file в основном предназна
чено для обеспечения соответствия требованиям объектноориентиро
ванного программирования (описывается далее в этой книге).
По сравнению с типами, с которыми вы уже знакомы, объекты файлов выглядят несколько необычно. Они не являются ни числами, ни по
следовательностями или отображениями – для задач работы с файла
ми они предоставляют одни только методы. Большинство методов файлов связаны с выполнением операций вводавывода во внешние файлы,
ассоциированные с объектом, но существуют также методы,
которые позволяют переходить на другую позицию в файле, выталки
вать на диск буферы вывода и т. д.
Открытие файловВ табл. 9.2 приводятся наиболее часто используемые операции над фай
лами. Чтобы открыть файл, программа должна вызвать функцию open,
передав ей имя внешнего файла и режим работы. Обычно в качестве режима используется строка 'r', когда файл открывается для чтения
Файлы
245
(по умолчанию), 'w' – когда файл открывается для записи, или 'a' –
когда файл открывается на запись в конец. Существуют и другие воз
можности, но мы не будем упоминать о них здесь. В некоторых случа
ях в конец строки режима может добавляться символ b, что позволяет работать с двоичными данными (интерпретация символов новой стро
ки отключена), а добавление символов a+ означает, что файл открыва
ется как для чтения, так и для записи (т. е. из файла можно читать и записывать в него с помощью одного и того же объекта).
Таблица 9.2. Часто используемые операции над файлами
Оба аргумента функции open должны быть строками. Кроме того, мо
жет использоваться третий необязательный аргумент, управляющий буферизацией выводимых данных, – значение ноль означает, что вы
ходная информация не будет буферизироваться (т. е. она будет записы
ваться во внешний файл сразу же, в момент вызова метода записи).
Имя внешнего файла может включать платформозависимые префиксы абсолютного или относительного пути к файлу. Если путь к файлу не указан, предполагается, что он находится в текущем рабочем каталоге
(т. е. в каталоге, где был запущен сценарий).
Операция
Интерпретация
output
= open('/tmp/spam', 'w') Открывает файл для записи ('w' означает write –
запись)
input = open('data', 'r')
Открывает файл для чтения ('r' означает read –
чтение)
input = open('data')
То же самое, что и в предыдущей строке (ре
жим 'r' используется по умолчанию)
aString = input.read()
Чтение файла целиком в единственную строку aString = input.read(N)
Чтение следующих N байтов (одного или более)
в строку aString = input.readline()
Чтение следующей текстовой строки (включая символ конца строки) в строку aList = input.readlines()
Чтение файла целиком в список строк output.write(aString)
Запись строки в файл output.writelines(aList)
Запись всех строк из списка в файл output.close()
Закрытие файла вручную (выполняется по окончании работы с файлом)
output.flush()
Выталкивает выходные буферы на диск, файл остается открытым anyFile.seek(N)
Изменяет текущую позицию в файле для сле
дующей операции, смещая ее на N байтов от на
чала файла.
246
Глава 9. Кортежи, файлы и все остальное
Использование файлов
Как только будет получен объект файла, вы можете вызывать его ме
тоды для выполнения операций чтения или записи. В любом случае содержимое файла в программах на языке Python принимает форму строк – операция чтения возвращает текст в строках, а метод записи принимает информацию в виде строк. Существует несколько разно
видностей методов чтения и записи, а в табл. 9.2 перечислены лишь наиболее часто используемые из них.
Несмотря на то, что методы чтения и записи, перечисленные в табли
це, являются наиболее часто используемыми, имейте в виду, что са
мый лучший, пожалуй, способ чтения строк из файла на сегодняшний день состоит в том, чтобы не читать файл вообще – как будет показано в главе 13, файлы имеют итератор (iterator), который автоматически читает информацию из файла строку за строкой в контексте цикла for,
в генераторах списков и в других итерационных контекстах.
Обратите внимание: данные, получаемые из файла, всегда попадают в сценарий в виде строки, поэтому вам необходимо будет выполнять преобразование данных в другие типы объектов языка Python, если эта форма представления вам не подходит. Точно так же при выполне
нии операции записи данных в файл, в отличие от инструкции print,
интерпретатор Python не выполняет автоматическое преобразование объектов в строки – вам необходимо передавать методам уже сформи
рованные строки. Поэтому при работе с файлами вам пригодятся рас
сматривавшиеся ранее инструменты преобразования данных из стро
кового представления в числовое и наоборот (например, int, float, str и выражения форматирования строк). Кроме того, в состав Python входят дополнительные стандартные библиотечные инструменты,
предназначенные для работы с универсальным объектом хранилища данных (например, модуль pickle) и обработки упакованных двоич
ных данных в файлах (например, модуль struct). С действием обоих модулей мы познакомимся ниже, в этой же главе.
Вызов метода close разрывает связь с внешним файлом. Как рассказы
валось в главе 6, интерпретатор Python немедленно освобождает па
мять, занятую объектом, как только в программе будет утеряна по
следняя ссылка на этот объект. Как только объект файла освобождает
ся, интерпретатор автоматически закрывает ассоциированный с ним файл. Благодаря этому вам не требуется закрывать файл вручную, осо
бенно в небольших сценариях, которые выполняются непродолжи
тельное время. С другой стороны, вызов метода close не повредит, и его рекомендуется использовать в крупных системах. Строго говоря, воз
можность автоматического закрытия файлов не является частью спе
цификации языка, и с течением времени такое поведение может изме
ниться. Следовательно, привычку вызывать метод close вручную мож
но только приветствовать.
Файлы
247Файлы в действииДавайте рассмотрим небольшие примеры, демонстрирующие основы работы с файлами. В первом примере выполняется открытие нового файла в режиме для записи, в него записывается строка (завершающая
ся символом новой строки \n), после чего файл закрывается. Далее этот же файл открывается в режиме для чтения и выполняется чтение стро
ки из него. Обратите внимание, что второй вызов метода readline воз
вращает пустую строку – таким способом методы файла в языке Python сообщают о том, что был достигнут конец файла (пустая строка в фай
ле возвращается как строка, содержащая единственный символ новой строки, а не как действительно пустая строка). Ниже приводится пол
ный листинг сеанса:
>>>
myfile = open('myfile', 'w') # Открывает файл для записи (создает файл)>>>
myfile.write('hello text file\n') # Записывает строку текста>>>
myfile.close() # Выталкивает выходные буферы на диск>>>
myfile = open('myfile') # Открывает файл для чтения: 'r' – по умолчанию>>>
myfile.readline() # Читает строку'hello text file\n'
>>>
myfile.readline() # Пустая строка: конец файла''
Этот пример записывает единственную строку текста в файл, добавляя в нее символ конца строки \n – методы записи не добавляют символ конца строки, поэтому нам необходимо добавлять его в выводимые строки (в противном случае следующая операция записи дополнит те
кущую строку в файле).
Сохранение и интерпретация объектов Python в файлахТеперь создадим большой файл. Следующий пример записывает раз
личные объекты в текстовый файл. Обратите внимание на использова
ние средств преобразования объектов в строки. Напомню, что данные
всегда записываются в файл в виде строк, а методы записи не выпол
няют автоматического преобразования данных:
>>>
X, Y, Z = 43, 44, 45 # Объекты языка Python должны >>>
S = 'Spam' # записываться в файл только в виде строк>>>
D = {'a': 1, 'b': 2}>>>
L = [1, 2, 3]>>>
>>>
F = open('datafile.txt', 'w') # Создает файл для записи>>>
F.write(S + '\n') # Строки завершаются символом \n>>>
F.write('%s,%s,%s\n' % (X, Y, Z)) # Преобразует числа в строки>>>
F.write(str(L) + '$' + str(D) + '\n') # Преобразует и разделяет символом $>>>
F.close()Создав файл, мы можем исследовать его содержимое, открыв файл и прочитав данные в строку (одной операцией). Обратите внимание,
что функция автоматического вывода в интерактивной оболочке дает точное побайтовое представление содержимого, а инструкция print
248Глава 9. Кортежи, файлы и все остальное интерпретирует встроенные символы конца строки, чтобы обеспечить более удобочитаемое отображение:
>>>
bytes = open('datafile.txt').read() # Отображение bytes >>>
bytes # в неформатированном виде"Spam\n43,44,45\n[1, 2, 3]${'a': 1, 'b': 2}\n"
>>>
print bytes # Удобочитаемое представление Spam
43,44,45
[1, 2, 3]${'a': 1, 'b': 2}
Теперь нам необходимо выполнить обратные преобразования, чтобы получить действительные объекты языка Python из строк в текстовом файле. Интерпретатор Python никогда автоматически не выполняет преобразование строк в числа или в объекты других типов, поэтому нам необходимо выполнить соответствующие преобразования, чтобы можно было использовать операции над этими объектами, такие как индексирование, сложение и т. д.:
>>>
F = open('datafile.txt') # Открыть файл снова>>>
line = F.readline() # Прочитать одну строку>>>
line'Spam\n'
>>>
line.rstrip() # Удалить символ конца строки'Spam'
Для этой первой строки был использован метод rstrip, чтобы удалить завершающий символ конца строки. Тот же эффект можно было бы получить с помощью извлечения среза line[:1], но такой подход мож
но использовать, только если мы уверены, что все строки завершаются символом \n (последняя строка в файле иногда может не содержать этого символа). Пока что мы прочитали ту часть файла, которая содер
жит строку. Теперь прочитаем следующий блок, в котором содержат
ся числа, и выполним разбор этого блока (т. е. извлечем объекты):
>>>
line = F.readline() # Следующая строка из файла>>>
line # Это – строка'43,44,45\n'
>>>