Удаление переменной из памяти Python
У меня технический вопрос. Пишу на Python. Две переменные задаются таким образом:
a = 5
b = a
del a
print(b)
Программа выводит:
5
Почему программа выводит 5, а не None? Ведь две переменные указывают на одну область памяти и мы ее удаляем, получается переменная b должна быть None. Почему она все-равно равняется 5? Очищается ли эта область памяти при вызове del или она переписывается в другое место, но как тогда ее очистить?
Ответы (3 шт):
Если мне память не изменяет:
Когда мы инициализируем переменную a = 5
то в памяти создаётся объект типа int
со значением 5
, и переменная a
начинает ссылаться на этот объект (хотя если в памяти уже был объект int=5 то в целях экономии памяти новый объект может и не создаться)
Теперь b = a
переменная b
начинает ссылаться на тот же объект, что и a
. И тут важно, что не происходит копирование переменной a
, а получается копирование ссылки на объект, которым является число 5
. В памяти в этот момент существует один объект — число 5
, и обе переменные ссылаются на него (а может и еще другие переменные на него ссылаются, но это уже оптимизации самого Python).
А вот сам del a
удаляет непосредственно переменную a
, но не объект, на который она ссылается. Поскольку на объект 5
по-прежнему ссылается переменная b
, объект не удаляется, и память не очищается.
И теперь самое очевидное print(b)
отработает поскольку переменная b
всё ещё указывает на объект 5
, ведь del a
просто удалил переменную a
, но не int 5
в памяти.
А вот непосредственно из памяти объекты удаляет сборщик мусора. В Python для удобства и автоматизации/оптимизации этот самый сборщик сам находит для очистки объекты, которые больше не имеют ссылок. В данном случае объект 5
остаётся в памяти до тех пор, пока существует хотя бы одна ссылка на него. А вот когда все ссылки на объект будут удалены (например, если мы также сделаем del b
или присвоим ей другое значение), объект станет "доступен для удаления", и сборщик мусора его выгрузит из памяти.
Так что если хотим полностью удалить объект из памяти, то придётся найти и удалить все ссылки на этот объект. В данном случае:
a = 5
b = a
del a
del b
Теперь нет ссылок на объект 5
и int 5
будет выгружен из памяти сборщиком мусора при следующей его ревизии объектов в памяти.
К слову, небольшие целые числа в диапазоне от -5 до 256 кэшируются интерпретатором и хранятся в специальном массиве, и они никогда не удаляются, поскольку часто используются.
Забавный пример:
>>> a = 10
>>> b = 300
>>> a is 10
True
>>> b is 300
False
Числа вне диапазона -5, 256 интерпретатор тоже будет пытаться оптимизировать, но только если они находятся в одной строке (или одном файле). В командной строке интерпретатора так не работает:
>>> d = 400
>>> d is 400
False
а так будет:
a = 5
b = 300
print(a is 5) # True
print(b is 300) # True
Так же в Python есть интернирование строк
— это своего рода оптимизация, которая позволяет уменьшить количество строковых объектов в памяти по средству кэширования некоторых строк (происходит автоматически для коротких строк).
a = "hello world!"
b = "hello world!"
print(a is b) # True
a = "hello my litl world is the good job, " * 300
v = "hello my litl world is the good job, " * 300
print(a is v) # False
z = f"hello {b}"
x = f"hello {b}"
print(z is x) # False
import sys
a = sys.intern(a)
v = sys.intern(v)
print(a is v) # Теперь True, строки интернированы вручную
z = sys.intern(z)
x = sys.intern(x)
print(z is x) # Тоже True
В общем, del не удаляет объект, а просто удаляет переменную и её копию ссылки на объект. Надеюсь понятно изложил?
Дополнительная иллюстрация:
import gc
class A():
def __repr__(self):
return 'Объект класса A'
def __del__(self):
print('Объект всё', flush=True)
a = A()
print(a, id(a), flush=True)
b = a
del a
print(b, id(b), flush=True)
del b
gc.collect()
Вывод:
Объект класса A 2108801486752
Объект класса A 2108801486752
Объект всё
От того, что вы удалили переменную a
, сам объект, на который она указывала, никуда не делся. Теперь на тот же самый объект указывает другая переменная b
. Вот когда на этот объект уже никто не будет указывать, тогда его (когда-нибудь) соберёт GC
, как уже написали. Ну либо он удалится, если вы напрямую вызовете gc
после удаления всех ссылок на объект.
P.S. Спасибо insolor, дописал про отслеживание собственно удаления.
Вам нужно активизировать сбор мусора после удаления переменной и почистить память после удаления ссылки на область памяти.
import gc
# Manual object cleaning
del x
gc.collect()