Саммерфилд - Программирование на Python 3 (1077331), страница 38
Текст из файла (страница 38)
Однако в некоторых ситуациях действительно бывает необходимо создать отдельную копию коллекции (то есть создать другой изменяемый объект). В случае последовательностей, когда выполняется оператор извлечения среза, например, вапдв[: 2], полученный срез — это всегда независимая копия элементов. Поэтому скопировать последовательность целиком можно следующим способом: »> вапдв = ["Ввсаьвв", "Ваув", "Сага1"] »> ьвас1ев = ваада[:] »> ьвас1вв[2] = "сауеппв" »> ЬваС1вв, вапдв (['Ввсаавв', 'Ваув', 'Саувппе'], ['Весаавв', 'Ваув', 'Сага1']) В случае словарей и множеств копирование можно выполнить с помощью методов ь[сс. сору() и вес. сору(). Кроме того, в модуле сору имеется функция сору. сору(), которая возвращает копию заданного объекта.
Другой способ копирования встроенных типов коллекций заключается в использовании имени типа как функции, которой в качестве аргумента передается копируемая коллекция. Например: сору аг ьссс и = ьссс(а) сару аг 1(вс ь = 1(вс(ь) сору аг вес з = вес(в) Обратите внимание, что все эти приемы копирования создают поверхносгпньге копии, то есть копируются только ссылки на объекты, но не Примеры сами объекты. Для неизменяемых типов данных, таких как числа и строки, это равносильно копированию (за исключением более высокой эффективности), но для изменяемых типов данных, таких как вложенные коллекции, это означает, что ссылки в оригинальной коллекции и в копии будут указывать на одни и те же объекты.
Эту особенность иллюстрирует следующий пример: »> х = (53, 68, [ "А", "В", "С")) »> у = х[.) В поверхностное копирование »> х, у ([53, 68, ['А', 'В', 'С')), [53, 68, ['А', 'В', 'С'))) »> у[!] = 40 »> х(2ЦО) = '0' »> х, у ((53, 68, ['0', 'В', 'С')), (53, 40, ['0', 'В', 'С'))) Когда выполняется поверхностное копирование списка х, копируется ссылка на вложенный список ("А", "В", "С" ]. Это означает, что третий элемент в обоих списках, х и у, ссылается на один и тот же список, поэтому любые изменения, произведенные во вложенном списке, можно наблюдать с помощью любой из ссылок, х или у.
Если действительно необходимо создать абсолютно независимую копию коллекции с произвольной глубиной вложенности, необходимо выполнить глубокое копирование: »> !прог! сору »> х = [53, 68, ("А", "В", "С")) »> у = сору свврсору(х) »> у[1) = 40 >» х[2)[0] = '0' >»х, у ((53, 68, ('0', 'В', 'С')], [53, 40, ['А', 'В', 'С'))) Здесь списки х и у, а также элементы, которые они содержат, полностью независимы. Обратите внимание: с этого момента мы будем использовать термины копия и поверхностная копия как взаимозаменяемые, а когда будет подразумеваться глубокое копирование, об этом будет упоминаться явно.
Примеры Мы завершили обзор встроенных типов коллекций в языке РуФоп„ а также двух типов, реализованных в стандартной библиотеке (сс11ес! тса8, вазезтар1е и 0011ес11008. Вегас1(Вас!), В языке Ру!)топ имеется также тип коллекций 0011ес!гора. серве, двухсторонней очереди, и многие другие типы коллекций, реализованные сторонними разработчиками и доступные в каталоге пакетов Ру!)топ Рас)!аде 1пдех, рурйруг)топ.огу] рур(. А сейчас мы рассмотрим пару немного более длинных примеров, 17б Глава 3, Типы коллекций в которых используется многое из того, о чем рассказывалось в этой и в предыдущей главах. Первая программа насчитывает примерно семьдесят строк и имеет отношение к обработке текстовой информации.
Вторая программа содержит примерно девяносто строк и предназначена для выполнения математических вычислений. Обе программы используют словари, списки, именованные кортежи и множества н обе широко используют метод 31г. То гваг( ), который был описан в предыдущей главе. ЯЯПЮГЗЫ Ц5б)ГПМНП)95.РУ Представьте, что мы выполняем настройку новой компьютерной системы и нам необходимо сгенерировать имена пользователей для всех служащих нашей компании.
У нас имеется простой текстовый файл (в кодировке Т)ТР-8), где каждая строка представляет собой запись из полей, разделенных двоеточиями. Каждая запись соответствует одному сотруднику компании и содержит следующие поля: уникальный идентификатор служащего, имя, отчество (это поле может быть пустым), фамилию и название отдела. Ниже в качестве примера приводятся несколько строк из файла гуа(а/иаегалхм 1601:А1сега: ЕоКаа;Моподовегуг Еада1 3702 А1ьегг;шкап:мопгдовегу,яа1аа 4730: Маае11а: ( апаа1а: Ха гепооа7 пд Мазе 10 Цаегпава Еапса1а, Масе11а.......
Мопгдовагу, А1сего Е... Мопгдовегу, А1зегг 1.... (4730) п1апаа1а (1601) а1вопгдо (3702) а1вопгдо1 Каждая запись имеет точно пять полей, и хотя можно обращаться к ним по числовым индексам, тем не менее мы будем использовать ос- мысленные имена, чтобы сделать программный код более понятным: 10, РОЛЕМАМЕ, М100ЕЕМАМЕ, 30ЯМАМЕ, 0ЕРАЛТМЕМТ = галде(6) В языке РУСЬоп общепринято использовать только символы верхнего регистра для идентификаторов, которые будут играть роль констант. Нам также необходимо создать тип именованного кортежа, где будут храниться данные о текущем пользователе: Программа должна читать данные из файла, который указан в командной строке, извлекать отдельные поля из каждой строки (записи) и возвращать подходящие имена пользователей. Каждое имя пользователя должно быть уникальным и должно создаваться на основе имени сотрудника. Результаты должны выводиться на консоль в текстовом виде, отсортированными в алфавитном порядке по фамилии и имени, например: Примеры Овег = со11есг!опв.
пааеосир1а("Овег", "иввгпааа Гогапаае и!ОО1епапе вигпаае га") Позднее, когда мы будем рассматривать оставшуюся часть программы, мы увидим, как используется именованный кортеж 0ве г и константы. Основная логика программы сосредоточена в функции аа1п(): оег пагп(): !Г 1еп(вув.агрч) == 1 ог вув.агрч[1] !и ("-П", "--Пе1р"): рыпг("ивара: (0) г!1е1 [(г1е2 [... г!1ем]]".(огаа1( вув дг9ч[0])) вув.ех!1() ивегпааев = вв1() ивегв = () Гог Гг1епапе 1п вув.агру[1:]'. Гог 1шв 1п преп((г1епаае, епсоп!пры'и1(8"); 1гпе = 1гпе гвгггр() !Г 11пе: ивег = ргосавв 1!пе( 11пе, ивегпааев) ивегв[(ивег,вигпааа.)очаг(), ивег.(огепаае. 1оиаг(), ивег.
10)] = иввг рг1пг ивегв(ивегв) Если пользователь не ввел в командной строке имя какого-нибудь файла или ввел параметр «-Ь» или « — Ье]р», то программа просто выводит текст сообщения с инструкцией о порядке использования и завершает работу. Из каждой прочитанной строки удаляютея любые завершающие пробельные символы [такие как уп), и обработка строки продолжается, только если она не пустая.
Это означает, что если в данных содержатся пустые строки, они будут просто проигнорированы. Все сгенерированные имена пользователей сохраняются в множестве ивегпавев, чтобы гарантировать отсутствие повторяющихся имен пользователей. Сами данные сохраняются в словаре ивегв. Информация о каждом пользователе сохраняется в виде элемента словаря, ключом которого является кортеж, содержащий фамилию сотрудника, его имя и идентификатор, а значением — именованный кортеж типа 0вег. Использование кортежа, содержащего фамилию сотрудника, его имя и идентификатор, в качестве ключа обеспечивает возможность вызывать функцию вог!ез() для словаря и получать итерируемый объект, в котором элементы будут упорядочены в требуемом нам порядке [то есть фамилия, имя, идентификатор), избежав необходимости создавать функцию, которую пришлось бы передавать в качестве аргумента Меу. Ое( ргосевв 1!се(1гпе, ивегпаасв).
Гга1пв = 11па.вр1!!( : ) ивегпааа = Оапегага ивегпаае(Г!е1пв, ивегпааев) ивег = Овег(ивегпааа, Гге1пв[ЕОНЕМАМЕ], Г!е1пв[М100[ЕМАМЕ], 178 Глава 3. Типы коллекций Гзе108[80НМАМЕ], 11е1св[10] ) гегигп ивег Поскольку все записи имеют очень простой формат, и мы уже удалили из строки завершающие пробельные символы, извлечь отдельные поля можно простой разбивкой строки по двоеточиям. Мы передаем список полей и множество иве гпамев в функцию депегз1е ивегпаае() и затем создаем экземпляр именованного кортежа две г, который возвращается вызывающей программе (функции аа!п()), которая в свою очередь вставляет информацию о пользователе в словарь иввгв, готовый для вывода на экран.
Если бы мы не создали соответствующие константы для хранения индексов, мы могли бы использовать числовые индексы, как показано ниже: ивег = Овег(ивегпаме, Гге108[1], Г~е1ив[2], 11е108[3], Г!е1св[О]) Хотя такой программный код занимает меньше места, тем не менее это не самое лучшее решение. Во-первых, человеку, который будет сопровождать такой программный код, непонятно, какое поле какую информацию содержит, а, во-вторых, такой программный код чувствителен к изменениям в формате файла с данными — если изменится порядок или число полей в записи, этот программный код окажется неработоспособен. При использовании констант в случае изменения структуры записи нам достаточно будет изменить только значения констант, и программа сохранит евою работоспособность. Ое( депега!е ивегпаме(11е108, ивегпааев).