Лутц М. - Изучаем Python (1077325), страница 38
Текст из файла (страница 38)
Напомню, что в языке Ру[)топ типы связаны с объектами, а не с именами. 'Гак как имена — это всего лишь ссылки на объекты, данный код работает. Во-вторых, следует помнить, что попутно уничтожаются ссылки на объекты. Каждый раз, когда имя х ассоциируется с новым объектом, интерпретатор Рус)топ освобождает пространство, занятое прежним объектом.
Например, когда с именем х связывается строка 'впгсззегу', 171 Разделяемые ссылки объект 42 немедленно уничтожается (при условии, что на него не ссылается никакое другое имя), а пространство памяти, занимаемое объектом, возвращается в пул свободной памяти для повторного использования в будущем, Достигается это за счет того, что в каждом объекте имеется счетчик ссылок, с помощью которого интерпретатор следит за количеством ссылок, указывающих на объект в настоящий момент времени. Как только (и именно в этот момент) значение счетчика достигает нуля, память, занимаемая объектом, автоматически освобождается. В предыдущем листинге мы исходили из предположения, что всякий раз, когда имя х ассоциируется с новым объектом, счетчик предыдущего объекта уменьшается до нуля, заставляя интерпретатор освобождать память.
Основная выгода от сборки мусора состоит в том, что программист может свободно распоряжаться объектами, не будучи обязан освобождать память в своем сценарии. Интерпретатор сам будет выполнять очистку неиспользуемой памяти в ходе выполнения программы. На практике эта особенность позволяет существенно уменьшить объем программного кода по сравнению с низкоуровневыми языками программирования, такими как С и С++. Разделяемые ссылки До сих пор мы рассматривали вариант, когда ссылка на объект присваивается единственной переменной.
Теперь введем в действие еще одну переменную и посмотрим, что происходит с именами и объектами в этом случае: »> а = 3 »> Ь = а В результате выполнения этих двух инструкций получается схема взаимоотношений, отраженная на рис. 6.2. Вторая инструкция вынуждает интерпретатор создать переменную Ь и использовать для инициализации переменную а, при этом она замещается объектом, на который Рис. 6.3. Имена и объекты после выполнения инструкции присваивания Ь - а. Переменная Ь превращается в ссылку на объект 3.
С технической точки зрения переменная в действительности является указателем на область памяти объекта, созданного в результате выполнения литерального выражения 3 Глава б. Интерпюдив о динамической типизации ссылается (3), и Ь превращается в ссылку на этот объект. В результате переменные в и Ь ссылаются на один и тот же объект (то есть указывают на одну и ту же область в памяти). В языке РуФ)топ это называется разделяемая ссылка — несколько имен ссылаются на один и тот же объект. Далее добавим еще одну инструкцию: »> а = 3 »> Ь = а »> а = 'враз' Как во всех случаях присваивания в языке РуФ)топ, в результате выполнения этой инструкции создается новый объект, представляющий строку ' зрап ', а ссылка на него записывается в переменную а.
Однако эти действия не оказывают влияния на переменную Ь вЂ” она по-прежнему ссылается на первый объект, целое число 3. В результате схема ссылок приобретает вид, показанный на рис. 6.3. То же самое произошло бы, если бы ссылка на объект 'враз' вместо переменной в была присвоена переменной Ь вЂ” изменилась бы только переменная Ь, но не в. Аналогичная ситуация возникает, даже если тип объекта не изменяется.
Например, рассмотрим следующие три инструкции: »> а = 3 »> Ь = а »>а=а+2 В этой последовательности происходят те же самые события: интерпретатор РуФ)зоп создает переменную в и записывает в нее ссылку на объект 3. После этого он создает переменную Ь и записывает в нее ту же ссылку, что хранится в переменной а, как показано на рис. 6.2. Наконец, последняя инструкция создает совершенно новый объект (в данном Рис.
6.3. Имена и обьекты после выполнения инструкции присваивания а = 'врат'. Переменная а ссылается на новый обьект (то есть на область памяти), созданный в результате выполнения литерального выражения 'врат', но переменная Ь по-прежнему ссылается на первый объект 3. Так как зта операция присваивания никак не изменяет обьгкт 3, она изменяет только переменную а, но не Ь 173 Разделяемые ссылки случае — целое число 5, которое является результатом выполнения операции сложения). Это не приводит к изменению переменной 5.
В действительности нет никакого способа перезаписать значение объекта 3— как говорилось в главе 4, целые числа относятся к категории неизменяемых и потому эти объекты невозможно изменить. Переменные в языке РуФЬоп, в отличие от других языков программирования, всегда являются указателями на объекты, а не метками областей памяти, доступных для изменения: запись нового значения в переменную не приводит к изменению первоначального объекта„но приводит к тому, что переменная начинает ссылаться на совершенно другой объект. В результате инструкция присваивания может воздействовать только на одну переменную. Однако когда в уравнении появляются изменяемые объекты и операции, их изменяющие, картина несколько меняется. Чтобы узнать как, давайте двинемся дальше.
Разделяемые ссылки и изменяемые объекты Как будет показано дальше в этой части книги, существуют такие объекты и операции, которые приводят к изменению самих объектов. Например, операция присваивания значения элементу списка фактически изменяет сам список вместо того, чтобы создавать совершенно новый объект списка. При работе с объектами, допускающими такие изменения, необходимо быть особенно внимательными при использовании разделяемых ссылок, так как изменение одного имени может отразиться на других именах.
Чтобы проиллюстрировать сказанное„возьмем в качестве примера объекты списков, о которых рассказывалось в главе 4. Напомню, что списки, которые поддерживают возможность присваивания значений элементам, — это просто коллекция объектов, которая в программном коде записывается как литерал в квадратных скобках: »>с1=с2, 3, 4) »> Сг= С1 В данном случае 1.1 — это список, содержащий объекты 2, 3 и 4. Доступ к элементам списка осуществляется по их индексам; так, ~1~0] ссылается на объект 2, первый элемент в списке 01. Безусловно, списки являются полноценными объектами, такими же, как целые числа и строки.
После выполнения двух приведенных выше инструкций 01 и 1.2 будут ссылаться на один и тот же объект, так же как переменные а и Ь в предыдущем примере (рис. 6.2). Точно так же, если теперь добавить еще одну инструкцию: »> Ы - "24 переменная 1.1 будет ссылаться на другой объект, а 1.2 по-прежнему будет ссылаться на первоначальный список. Однако если синтаксис последней инструкции чуть-чуть изменить, эффект получится радикально другим; Глава б. Интерлюдия о динамической типизации »> ь1 = [2, 3, 4] № Изиеняеный объект »> ь2 ь1 № Создание второй ссылки на тот ме саиый обьект »> ь1[0] = 24 № Изменение обьекта »> ьз [24, 3, 4] »> ь2 [24, 3, 4] № Переменная ( 1 изменилась № Но также изменилась и переиенная (2( Здесь мы не изменяем сам объект [1, изменяется компонент объекта, на который ссылается [1.
Данное изменение затронуло часть самого объекта списка. Поскольку объект списка разделяется разными переменными (ссылки на него находятся в разных переменных), то изменения в самом списке затрагивают не только [1, то есть следует понимать, что такие изменения могут сказываться в других частях программы. Вэтом примере изменения обнаруживаются также в переменной [2, потому что она ссылается на тот же самый объект, что и [1. Здесь мы фактически не изменяли [2, но значение этой переменной изменилось. Как правило, это именно то, что требовалось, но вы должны понимать, как это происходит.
Это — поведение по умолчанию: если вас оно не устраивает„можно потребовать от интерпретатора, чтобы вместо создания ссылок он выполнял копирование объектов. Скопировать список можно несколькими способами, включая встроенную функцию ]131 и модуль сору из стандартной библиотеки.
Однако самым стандартным способом копирования является получение среза от начала и до конца списка (подробнее об этой операции рассказывается в главах 4 и 7): »> ь1 = [2, 3, 4] »> ь2 = 'ь1[:] № Создается копия списка ( 1 >» ь1[0] = 24 »> ь1 [24. 3, 4] »> ь2 [2, 3, 4] № [2 не изменилась [врогт сору Х = сору. сору(У) № Создание "поверхностной" копии любого обьекта у Х = сору Оеерсору(У) № Создание полной копии: копиртются все влоиенные части Здесь изменения в [1 никак не отражаются на [2, потому что [2 ссылается на копию объекта, на который ссылается переменная [1.
То есть эти переменные указывают на различные области памяти. Обратите внимание: способ, основанный на получении среза, неприменим в случае с другим изменяемым базовым типом — со словарями, потому что словари не являются последовательностями. Чтобы скопировать словарь, необходимо воспользоваться методом О. сору(). Следует также отметить, что модуль сору из стандартной библиотеки имеет в своем составе универсальную функцию, позволяющую копировать объекты любых типов, включая вложенные структуры (например, словари с вложенными списками): Разделяемые ссылки В главах 8 и 9 мы будем рассматривать списки и словари во всей полноте и там же вернемся к концепции разделяемых ссылок и копирования.