Лутц М. - Изучаем Python (1077325), страница 56
Текст из файла (страница 56)
253 Гибкость объектов Придется держать в уме: перегрузка операторов Позднее мы увидим, что объекты, реализованные с помощью классов, могут свободно попадать в любую из этих категорий. Например, если нам потребуется реализовать новый тип объектов последовательностей, который был бы совместим со встроенными типами последовательностей, мы могли бы описать класс, который перегружает операторы индексирования и конкатенации: с1авв МуВесоелсе: Оет ретттев (ве1т, тлсех): а Везивается лри выполнении операции ве111тлеех) оет аоо (ве1т, отяег); а Вызывается лри выполнении операции ве1т г отяег и т.
д. Точно так же можно было бы создать новый изменяемый или неизменяемый объект, выборочно реализовав методы, вызываемые для выполнения операций непосредственного изменения (например, для выполнения операций присваивания ве1ГГзлзех) ха1це вызывается метод вет)тев ). Существует также возможность выполнять реализацию новых объектов на других языках программирования, таких как С, в виде расширений. Чтобы определить выбор между множествами операций над числами, последовательностями и отображениями, необходимо будет подставить указатели на Функции С в ячейки структуры. Гибкость объектов В этой части книги были представлены несколько составных типов объектов (коллекции с компонентами). В общем случае: ° Списки, словари и кортежи могут хранить объекты любых типов.
° Списки, словари и кортежи допускают произвольную вложенность. ° Списки и словари могут динамически увеличиваться и уменьшаться. Поскольку составные типы объектов в языке Ру1)топ поддерживают любые структуры данных, они прекрасно подходят для представления в программах данных со сложной структурой. Например, значениями элементов словарей могут быть списки, которые могут содержать кортежи, которые в свою очередь могут содержать словари, и т. д.
Вложенность может быть произвольной глубины, какая только потребуется, чтобы смоделировать структуру обрабатываемых данных. Рассмотрим пример с вложенными компонентами. В следующем примере определяется три вложенных составных объекта последовательностей, как показано на рис. 9.1. Для обращения к отдельным элементам допускается использовать столько индексов, сколько потребуется.
В языке Ру$)топ разыменование индексов производится слева направо 254 Глава 9. Кортежи, файлы и все остальное Рис. 9.1. Дерево вложенных обэектов со смещениями их компонентов, созданное с помощью литерального выражения ('аус', 1'[1, 2), [1'2), 4)), 5). Синтаксически вложенные обэекты внутри реализованы в виде ссылок [т.
е. в виде указателей) на отдеяьные участки памяти и на каждом шаге извлекается объект на более глубоком уровне. Структура данных на рис. 9.1 может показаться излишне сложной, но она иллюстрирует синтаксис, который используется для доступа к данным: »> [ = ['аэо', [(1, 2), ([3], 4)], З] »> [[1] [(1, 2), ([3], 4)] »> [[1П1] ([3], 4) »> [[1П1ПЬ] [3] »> [[1П1ПоПс] 3 ССЫПКИ И КОПИИ В главе 6 говорилось, что при выполнении операции присваивания всегда сохраняется ссылка на объект, а не копия самого объекта. В большинстве случаев именно это нам и требуется.
Однако в процессе выполнения операций присваиваний может быть создано несколько ссылок на один и тот же объект, поэтому очень важно понимать, что изменения, произведенные в изменяемом объекте с помощью одной ссылки, отразятся и на других ссылках, указывающих на этот же объект. Если такой порядок вещей является нежелательным, вам необходимо явно сообщить интерпретатору, чтобы он создал копию объекта. Мы изучили это явление в главе 6, но ситуация может обостриться, когда в игру вступят крупные объекты. Например, в следующем приме- 256 Глава 9.
Кортежи, файлы и все остальное ° Выражение извлечения среза с пустыми пределами ([(:)) создает копию последовательности. ° Метод словаря сору (О, сору()) создает копию словаря. ° некоторые встроенные функции, такие как 11вт (1131([)), создают копию списка. ° Модуль сору, входящий в состав стандартной библиотеки, создает полные копии объектов. Например, предположим, что у нас имеются список и словарь, и для нас нежелательно изменение их значений посредством других переменных: »> ! = [1,2,3) »> 0 ('а':1, 'Ь':2) Чтобы предотвратить такую возможность, достаточно связать с други- ми переменными копии объектов вместо того, чтобы связать с ними са- ми объекты: Ф Вместо А = ! [или )зет<!)) и Вместо В = Р »> А "- 1.[: ) »> В = 0.ооау() В этом случае изменения, произведенные с помощью других перемен- ных, повлияют на копии, а не на оригиналы: »> А[1] = 'И!' »> В['с') = 'арав' »> »> Ц 0 (( 1, 2, 3), ('а': 1, 'Ь': 2)) >»А, В ((1, 'И!', 3), ('а': 1, 'с': 'врав', 'Ь': 2)) Если вернуться к первоначальному примеру, то избежать побочных эффектов, связанных со ссылками, можно, использовав выражение извлечения среза из оригинального списка: »>Х=[1, 2, 3) »> ! = ['а', х[:), 'ь') В Вотроеннме копии обьекта х >» 0 = ('х':Х[:), 'у''.2) Одно замечание по поводу копий: выражение извлечения среза с пустыми значениями пределов и метод словаря сору создают поверхиоот.
ные копии — т. е. они не копируют вложенные структуры данных, даже если таковые присутствуют. Если необходима полная копия структуры произвольной глубины вложенности, следует использовать стандартный модуль сору: добавьте инструкцию !врос! сору и вставьте выражение Х = сору. Реерсору(У), которое создаст полную копию объекта Это меняет картину, представленную на рис.
9.2, — ! и 0 теперь ссылаются на другие списки, отличные от Х. Теперь изменения, выполненные с помощью переменной Х, отразятся только на самой переменной Х, но не на [ и 0; точно так же изменения в [ или 0 никак не будут воздействовать на Х. 257 Сравнения, равенство и истина у со сколь угодно большой глубиной вложенности. Эта функция выполняет рекурсивный обход объектов и копирует все составные части.
Однако такой подход используется намного реже (потому что для этого приходится писать дополнительный программный код). Обычно ссылки — это именно то, что нам требуется, в противном случае чаще всего применяются такие способы копирования, как операция извлечения среза и метод сору. Сравнения, равенство и истина Любые объекты языка РуФ)топ поддерживают операции сравнения: проверка на равенство, относительная величина и т.
д. Операции сравнения в языке Руь[топ всегда проверяют все части составных объектов, пока результат не станет определенным. В действительности, для вложенных объектов интерпретатор Ру()>оп всегда автоматически выполняет обход структуры данных, чтобы применить операции сравнения регвурсивно, слева направо и настолько глубоко, насколько это необходимо.
Первое найденное различие определяет результат сравнения. Например, при сравнении списков автоматически сравниваются все их компоненты: »> Ш = [1, ('а', 3)) »> [2 = [1, ('а', 3)) »> ь1 == ь2, ь1 [в 'ь2 (Тгое, та)вв) В Рваные обьекты с одинаковыми значениями В Эквивалентны? Это один и тот ие объект? »> 81 = 'враз' »> 82 = 'враз' »> 81 == 82, 81 1в 82 (Тгве, Тгое) Здесь у нас также присутствуют два различных объекта, имеющие одинаковые значения: оператор == должна вернуть истину, а оператор В данном случае [1 и [2 ссылаются на совершенно разные списки, но с одинаковым содержимым. Из-за природы ссылок в языке Ру[)ьоп существует два способа проверки на равенство: ° Оператор == проверяет равенство значений.
Интерпретатор выполняет проверку на равенство, рекурсивно сравнивая все вложенные объекты. ° Оператор [а проверяет идентичность объектов. Интерпретатор проверяет, являются ли сравниваемые объекты одним и тем же объектом (т. е. расположены ли они по одному и тому же адресу в памяти). В предыдущем примере Ь1 и Ь2 прошли проверку == на равенство (они имеют одинаковое значение, потому что все их компоненты равны), но не прошли проверку 13 на эквивалентность (они ссылаются на разные объекты и, соответственно, на разные области памяти).
Однако, обратите внимание, что происходит при сравнении двух коротких строк: 258 Глава 9. Кортежи, файлы и эсе остальное 13 — ложь. Но так как интерпретатор с целью оптимизации кэширует и повторно использует короткие строки, в действительности обе они являются одной и той же строкой 'зраш' в памяти, которая совместно используется переменными 81 и 32, поэтому оператор [3 проверки идентичности возвращает истину.
Чтобы получить нормальное поведение, потребуется использовать более длинные строки: >» 31 = 'а 10лЯог 51г!ПД »> 82 = 'а 1олдег асг1лд' >» 81 == 32, 81 1а 32 (Тгие. Га)зо) Разумеется, так как строки являются неизменяемыми, использование механизма кэширования объектов не оказывает отрицательного влияния на ваш программный код — строки нельзя изменить непосредственно независимо от того, сколько переменных ссылаются на них. Если результат проверки на идентичность кажется вам обескураживающим, вернитесь к главе 6, чтобы освежить в памяти концепцию ссылок на объекты. Как правило, оператор == — это именно то, что требуется в большинстве случаев проверки равенства; оператор 13 предназначен для особых случаев.
Мы рассмотрим случаи использования этих операторов далее в книге. Операторы отношений к вложенным структурам также применяются рекурсивно: »> ь! = [1, ('а', 3)) »> (2 = [1, ('а', 2)) »> ь! < ь2, ь! =к 32, [! > ь2 (Га)зо, та[за, Тгое) Ф йоньшо, равно, больше: кортош результатов Здесь (.! больше, чем с2, потому что вложенное число 3 больше, чем 2. Результат последней строки в этом примере в действительности представляет собой кортеж из трех объектов — результатов трех выражений (пример кортежа без объемлющих круглых скобок). В общем случае Ру([топ сравнивает типы следующим образом: ° Числа сравниваются по величине.