Главная страница

Математический анализ. 3е издание


Скачать 4.86 Mb.
Название3е издание
АнкорМатематический анализ
Дата04.02.2022
Размер4.86 Mb.
Формат файлаpdf
Имя файлаpython_01.pdf
ТипДокументы
#351981
страница31 из 98
1   ...   27   28   29   30   31   32   33   34   ...   98
parts = line.split(',') # Разбить на подстроки по запятым
>>> parts
['43', '44', '45\n']
Здесь был использован строковый метод split, чтобы разбить строку на части по запятым, которые играют роль символовразделителей –
в результате мы получили список строк, каждая из которых содержит отдельное число. Теперь нам необходимо преобразовать эти строки в целые числа, чтобы можно было выполнять математические опера
ции над ними:
>>> int(parts[1]) # Преобразовать строку в целое число
44
>>> numbers = [int(P) for P in parts] # Преобразовать весь список

Файлы
249
>>> numbers
[43, 44, 45]
Как мы уже знаем, функция int преобразует строку цифр в объект це
лого числа, а генератор списков, представленный в главе 4, позволяет применить функцию ко всем элементам списка в одной инструкции
(подробнее о генераторах списков читайте далее в этой книге). Обрати
те внимание: для удаления завершающего символа \n в конце послед
ней подстроки не был использован метод rstrip, потому что int и неко
торые другие функции преобразования просто игнорируют символы
разделители, окружающие цифры.
Для преобразования списка и словаря в третьей строке файла восполь
зуемся встроенной функцией eval, интерпретирущей строку как код на Python (формально строка содержит выражение на языке Python):
>>> line = F.readline()
>>> line
"[1, 2, 3]${'a': 1, 'b': 2}\n"
>>> parts = line.split('$') # Разбить на строки по символу $
>>> parts
['[1, 2, 3]', "{'a': 1, 'b': 2}\n"]
>>> eval(parts[0]) # Преобразовать строку в объект
[1, 2, 3]
>>> objects = [eval(P) for P in parts] # То же самое для всех строк в списке
>>> objects
[[1, 2, 3], {'a': 1, 'b': 2}]
Поскольку теперь все данные представляют собой список обычных объектов, а не строк, мы сможем применять к ним операции списков и словарей.
Сохранение объектов Python с помощью модуля pickle
Функция eval, использованная в предыдущем примере для преобразо
вания строк в объекты, представляет собой мощный инструмент. И ино
гда даже слишком мощный. Функция eval без лишних вопросов вы
полнит любое выражение на языке Python, даже если в результате бу
дут удалены все файлы в компьютере, если передать в выражение со
ответствующие права доступа! Если вам действительно необходимо извлекать объекты Python из файлов, но вы не можете доверять источ
нику этих файлов, идеальным решением будет использование модуля pickle
, входящего в состав стандартной библиотеки Python.
Модуль pickle позволяет сохранять в файлах практически любые объек
ты Python без необходимости с нашей стороны выполнять какиелибо преобразования. Он напоминает суперуниверсальную утилиту форма
тирования и преобразования данных. Чтобы сохранить словарь в файле,
например, мы передаем его непосредственно в функцию модуля pickle:
>>> F = open('datafile.txt', 'w')
>>> import pickle

250
Глава 9. Кортежи, файлы и все остальное
>>> pickle.dump(D, F) # Модуль pickle запишет в файл любой объект
>>> F.close()
Чтобы потом прочитать словарь обратно, можно просто еще раз вос
пользоваться возможностями модуля pickle:
>>> F = open('datafile.txt')
>>> E = pickle.load(F) # Загружает любые объекты из файла
>>> E
{'a': 1, 'b': 2}
Нам удалось получить назад точно такой же объект словаря без необ
ходимости вручную выполнять какиелибо преобразования. Модуль pickle выполняет то, что называется сериализацией объектов – преоб
разование объектов в строку и из строки байтов, не требуя от нас почти никаких действий. В действительности, внутренняя реализация моду
ля pickle выполнила преобразование нашего словаря в строку, при этом незаметно для нас (и может выполнить еще более замысловатые преобразования при использовании модуля в других режимах):
>>> open('datafile.txt').read()
"(dp0\nS'a'\np1\nI1\nsS'b'\np2\nI2\ns."
Поскольку модуль pickle умеет реконструировать объекты из строково
го представления, нам не требуется самим возиться с этим. Дополни
тельную информацию о модуле pickle вы найдете в руководстве по стандартной библиотеке языка Python или попробовав импортировать модуль в интерактивном сеансе и передав его имя функции help. Когда будете заниматься исследованием этого модуля, обратите также внима
ние на модуль shelve – инструмент, который использует модуль pickle для сохранения объектов Python в файлах с доступом по ключу, описа
ние которых далеко выходит за рамки этой книги.
Сохранение и интерпретация упакованных
двоичных данных в файлах
Прежде чем двинуться дальше, необходимо рассмотреть еще один ас
пект работы с файлами: в некоторых приложениях приходится иметь дело с упакованными двоичными данными, которые создаются, на
пример, программами на языке C. Для их сохранения и восстанов
ления можно воспользоваться модулем struct из стандартной библио
теки языка Python. В некотором смысле это совершенно другой инст
румент преобразования данных, интерпретирующий строки в файлах как двоичные данные.
Например, чтобы создать файл с упакованными двоичными данными,
откройте его в режиме 'wb' (write binary – запись двоичных данных)
и передайте модулю struct строку форматирования и некоторый объект
Python. В следующем примере используется строка форматирования,
которая определяет пакет данных как содержащий 4байтовое целое число, 4символьную строку и 2байтовое целое число, причем все пред
ставлены в формате bigendian – в порядке следования байтов «от стар

Файлы
251
шего к младшему» (существуют также спецификаторы форматов, кото
рые поддерживают наличие символов дополнения слева, числа с пла
вающей точкой и многие другие):
>>> F = open('data.bin', 'wb') # Открыть файл для записи в двоичном режиме
>>> import struct
>>> bytes = struct.pack('>i4sh', 7, 'spam', 8) # Создать пакет двоичных данных
>>> bytes
'\x00\x00\x00\x07spam\x00\x08'
>>> F.write(bytes) # Записать строку байтов
>>> F.close()
Интерпретатор Python создает строку двоичных данных, которая затем записывается в файл обычным способом (строка состоит из экранирован
ных последовательностей, представляющих шестнадцатеричные значе
ния). Чтобы преобразовать эти значения в обычные объекты языка Py
thon, достаточно просто прочитать строку обратно и распаковать ее с ис
пользованием той же строки формата. Следующий фрагмент извлекает значения, преобразуя их в обычные объекты (целые числа и строка):
>>> F = open('data.bin', 'rb')
>>> data = F.read() # Получить упакованные двоичные данные
>>> data
'\x00\x00\x00\x07spam\x00\x08'
>>> values = struct.unpack('>i4sh', data) # Преобразовать в объекты
>>> values
(7, 'spam', 8)
Файлы с двоичными данными относятся к категории низкоуровневых средств, которые мы не будем рассматривать подробно. За дополни
тельной информацией обращайтесь к руководству по библиотеке язы
ка Python или импортируйте модуль struct в интерактивном сеансе и передайте имя struct функции help. Обратите также внимание на то,
что режимы доступа к двоичным файлам 'wb' и 'rb' могут использо
ваться для обработки простейших двоичных файлов, таких как изо
бражения или аудиофайлы, без необходимости выполнять распаковку их содержимого.
Другие инструменты для работы с файлами
Как показано в табл. 9.2, существуют более сложные инструменты для работы с файлами, более того, существуют и другие инструменты, ко
торые отсутствуют в таблице. Например, функция seek переустанавли
вает текущую позицию в файле (которая будет использована в следую
щей операции чтения или записи), функция flush принудительно вы
талкивает содержимое выходных буферов на диск (по умолчанию фай
лы всегда буферизуются) и т. д.
Руководство по стандартной библиотеке и книги, упомянутые в преди
словии, содержат полный перечень методов для работы с файлами.
Чтобы кратко ознакомиться с ним, можно также воспользоваться функцией dir или help в интерактивном сеансе, передав ей слово file

252
Глава 9. Кортежи, файлы и все остальное
(имя типа объектов). Дополнительные примеры работы с файлами вы найдете во врезке «Придется держать в уме: сканирование файлов»
в главе 13. В ней приводятся типичные примеры организации циклов для просмотра содержимого файлов и приводятся инструкции, кото
рые мы еще не рассматривали.
Следует также отметить, что функция open и объекты файлов, которые она возвращает, являются в языке Python основным интерфейсом к внешним файлам, однако в арсенале Python существуют и другие ин
струменты, напоминающие файлы. Кроме того, имеется возможность взаимодействовать с дескрипторами файлов (целочисленные значения,
которые поддерживаются такими низкоуровневыми средствами, как механизм блокировки файлов), реализованная в модуле os; сокетами,
каналами и файлами FIFO (объекты, по своим характеристикам напо
минающие файлы, используемые для синхронизации процессов или организации взаимодействий по сети); с файлами с доступом по ключу,
известными как «хранилища» («shelves») и используемыми для хране
ния объектов языка Python по ключу; и многими другими. За получе
нием дополнительной информации обо всех этих инструментах обра
щайтесь к специализированной литературе по языку Python.
Пересмотренный перечень категорий типов
Теперь, когда мы познакомились со всеми встроенными базовыми ти
пами языка Python в действии, давайте завершим наше турне рассмот
рением некоторых общих для них свойств.
В табл. 9.3 приводится классификация всех рассмотренных типов по категориям, которые были введены ранее. Над объектами одной кате
гории могут выполняться одни и те же операции, например, над стро
ками, списками и кортежами можно выполнять операции для последо
вательностей. Непосредственное изменение допускают только изменяе
мые объекты (списки и словари) – вы не сможете непосредственно из
менить числа, строки и кортежи. Файлы только экспортируют методы,
поэтому понятие изменяемости к ним понастоящему неприменимо –
хотя они могут изменяться при выполнении операции записи, но это не имеет никакого отношения к ограничениям типов в языке Python.
Таблица 9.3. Классификация объектов
Тип объектов
Категория
Изменяемый?
Числа
Числа
Нет
Строки
Последовательности
Нет
Списки
Последовательности
Да
Словари
Отображения
Да
Кортежи
Последовательности
Нет
Файлы
Расширения
Понятие неприменимо

Гибкость объектов
253
Гибкость объектов
В этой части книги были представлены несколько составных типов объектов (коллекции с компонентами). В общем случае:

Списки, словари и кортежи могут хранить объекты любых типов.

Списки, словари и кортежи допускают произвольную вложенность.

Списки и словари могут динамически увеличиваться и уменьшаться.
Поскольку составные типы объектов в языке Python поддерживают любые структуры данных, они прекрасно подходят для представления в программах данных со сложной структурой. Например, значениями элементов словарей могут быть списки, которые могут содержать кор
тежи, которые в свою очередь могут содержать словари, и т. д. Вло
женность может быть произвольной глубины, какая только потребует
ся, чтобы смоделировать структуру обрабатываемых данных.
Рассмотрим пример с вложенными компонентами. В следующем при
мере определяется три вложенных составных объекта последователь
ностей, как показано на рис. 9.1. Для обращения к отдельным элемен
там допускается использовать столько индексов, сколько потребуется.
В языке Python разыменование индексов производится слева направо
Придется держать в уме: перегрузка операторов
Позднее мы увидим, что объекты, реализованные с помощью классов, могут свободно попадать в любую из этих категорий. На
пример, если нам потребуется реализовать новый тип объектов последовательностей, который был бы совместим со встроенны
ми типами последовательностей, мы могли бы описать класс, ко
торый перегружает операторы индексирования и конкатенации:
class MySequence:
def __getitem__(self, index):
# Вызывается при выполнении операции self[index] def __add__(self, other):
# Вызывается при выполнении операции self + other и т. д. Точно так же можно было бы создать новый изменяемый или неизменяемый объект, выборочно реализовав методы, вызы
ваемые для выполнения операций непосредственного изменения
(например, для выполнения операций присваивания self[index]
= value вызывается метод __setitem__). Существует также воз
можность выполнять реализацию новых объектов на других языках программирования, таких как C, в виде расширений.
Чтобы определить выбор между множествами операций над чис
лами, последовательностями и отображениями, необходимо бу
дет подставить указатели на функции C в ячейки структуры.

254
Глава 9. Кортежи, файлы и все остальное и на каждом шаге извлекается объект на более глубоком уровне. Струк
тура данных на рис. 9.1 может показаться излишне сложной, но она иллюстрирует синтаксис, который используется для доступа к данным:
>>> L = ['abc', [(1, 2), ([3], 4)], 5]
>>> L[1]
[(1, 2), ([3], 4)]
>>> L[1][1]
([3], 4)
>>> L[1][1][0]
[3]
>>> L[1][1][0][0]
3
Ссылки и копии
В главе 6 говорилось, что при выполнении операции присваивания все
гда сохраняется ссылка на объект, а не копия самого объекта. В боль
шинстве случаев именно это нам и требуется. Однако в процессе выпол
нения операций присваиваний может быть создано несколько ссылок на один и тот же объект, поэтому очень важно понимать, что измене
ния, произведенные в изменяемом объекте с помощью одной ссылки,
отразятся и на других ссылках, указывающих на этот же объект. Если такой порядок вещей является нежелательным, вам необходимо явно сообщить интерпретатору, чтобы он создал копию объекта.
Мы изучили это явление в главе 6, но ситуация может обостриться, ко
гда в игру вступят крупные объекты. Например, в следующем приме
Рис. 9.1. Дерево вложенных объектов со смещениями их компонентов,
созданное с помощью литерального выражения ['abc', [(1, 2), ([3], 4)], 5].
Синтаксически вложенные объекты внутри реализованы в виде ссылок
(т. е. в виде указателей) на отдельные участки памяти
abc
5 4
3 2
1
[0]
[1]
[2]
[0]
[1]
[0]
[1]
[0]
[0]
[1]

Гибкость объектов
255
ре создается список, которому ставится в соответствие переменная X,
затем создается другой список, которому ставится в соответствие пере
менная L, который включает ссылку на список X. Далее создается сло
варь D, который содержит еще одну ссылку на список X:
>>> X = [1, 2, 3]
>>> L = ['a', X, 'b'] # Встроенная ссылка на объект X
>>> D = {'x':X, 'y':2}
В этот момент существует три ссылки на список, который был создан первым: из переменной X, из элемента списка, соответствующего пере
менной L, и из элемента словаря, соответствующего переменной D. Эта ситуация показана на рис. 9.2.
Так как списки относятся к категории изменяемых объектов, измене
ния в объекте списка, произведенные с помощью любой из трех ссы
лок, также отразятся на других двух ссылках:
>>> X[1] = 'surprise' # Изменяет все три ссылки!
>>> L
['a', [1, 'surprise', 3], 'b']
>>> D
{'x': [1, 'surprise', 3], 'y': 2}
Ссылки – это более высокоуровневый аналог указателей в других язы
ках программирования. И хотя вы не можете извлечь непосредствен
ное значение самой ссылки, тем не менее, одну и ту же ссылку можно сохранить более чем в одном месте (в переменных, в списках и т. д.).
Это полезная особенность – вы можете передавать крупные объекты между компонентами программы без выполнения дорогостоящей опе
рации копирования. Однако, когда действительно необходимо создать копию объекта, вы можете запросить их:
Рис. 9.2. Разделяемые ссылки на объект: поскольку переменная X,
элемент списка L и элемент словаря D ссылаются на один и тот же список,
то изменения, произведенные с помощью X, отразятся также на L и D
Имена
Объекты
1
?
3
L
X
D

256
Глава 9. Кортежи, файлы и все остальное

Выражение извлечения среза с пустыми пределами (L[:]) создает копию последовательности.

Метод словаря copy (D.copy()) создает копию словаря.

Некоторые встроенные функции, такие как list (list(L)), создают копию списка.

Модуль copy, входящий в состав стандартной библиотеки, создает полные копии объектов.
Например, предположим, что у нас имеются список и словарь, и для нас нежелательно изменение их значений посредством других переменных:
>>> L = [1,2,3]
>>> D = {'a':1, 'b':2}
Чтобы предотвратить такую возможность, достаточно связать с други
ми переменными копии объектов вместо того, чтобы связать с ними са
ми объекты:
>>> A = L[:] # Вместо A = L (или list(L))
>>> B = D.copy() # Вместо B = D
В этом случае изменения, произведенные с помощью других перемен
ных, повлияют на копии, а не на оригиналы:
>>> A[1] = 'Ni'
>>> B['c'] = 'spam'
>>>
>>> L, D
([1, 2, 3], {'a': 1, 'b': 2})
>>> A, B
([1, 'Ni', 3], {'a': 1, 'c': 'spam', 'b': 2})
Если вернуться к первоначальному примеру, то избежать побочных эффектов, связанных со ссылками, можно, использовав выражение извлечения среза из оригинального списка:
>>> X = [1, 2, 3]
>>>
1   ...   27   28   29   30   31   32   33   34   ...   98


написать администратору сайта