Ссылки в Python и счетчики ссылок
В этом руководстве вы получите представление о ссылках в Python и подсчете ссылок.
Введение в ссылки на Python
В Python переменная не является меткой значения, как вы думаете. Вместо этого переменная ссылается на объект, содержащий значение. Другими словами, переменные являются ссылками.
В следующем примере переменной присваивается число со значением 100:
counter = 100
Python создает в памяти новый целочисленный объект(int) и привязывает переменную counter к этому адресу памяти:
Когда вы получаете доступ к переменной counter, Python ищет объект, на который ссылается счетчик, и возвращает значение этого объекта:
print(counter) # 100
Таким образом, переменные — это ссылки, указывающие на объекты в памяти.
Чтобы найти адрес памяти объекта, на который ссылается переменная, вы передаете переменную встроенной функции id().
Например, следующая команда возвращает адрес памяти целочисленного объекта, на который ссылается переменная counter:
counter = 100 print(id(counter))
Выход:
140717671523072
Функция id() возвращает адрес памяти объекта, на который ссылается переменная, в виде числа по основанию 10.
Чтобы преобразовать этот адрес памяти в шестнадцатеричную строку, вы используете функцию hex():
counter = 100 print(id(counter)) print(hex(id(counter)))
Выход:
140717671523072 0x7ffb62d32300
Подсчет ссылок
Объект по адресу памяти может иметь одну или несколько ссылок. Например:
counter = 100
Целочисленный объект со значением 100 имеет одну ссылку, которая является переменной-счетчиком. Если вы присвоите счетчик другой переменной, например max:
counter = 100 max = counter
Теперь переменные counter и max ссылаются на один и тот же целочисленный объект. Целочисленный объект со значением 100 имеет две ссылки:
Если вы присвоите другое значение переменной max:
max = 999
…целочисленный объект со значением 100 будет иметь одну ссылку, которая является переменной-счетчиком:
А количество ссылок целочисленного объекта со значением 100 будет равно нулю, если вы присвоите переменной-счетчику другое значение:
counter = 1
Если у объекта нет ссылки, диспетчер памяти Python уничтожит этот объект и освободит память.
Как подсчитать количество ссылок
Чтобы получить количество ссылок на объект, вы используете метод from_address() модуля ctypes.
ctypes.c_long.from_address(address).value
Чтобы использовать этот метод, вам необходимо передать адрес памяти объекта, ссылки на который вы хотите подсчитать. Кроме того, адрес должен быть целым числом.
Ниже определяется функция ref_count(), которая использует метод from_address():
import ctypes def ref_count(address): return ctypes.c_long.from_address(address).value
Теперь вы можете использовать более короткую функцию ref_count() вместо длинного синтаксиса, как указано выше.
В этом примере определяется список из трех целых чисел:
numbers = [1, 2, 3]
Чтобы получить адрес памяти списка номеров, вы используете функцию id() следующим образом:
numbers_id = id(numbers)
Ниже показано количество ссылок на список, на который ссылается переменная Numbers:
print(ref_count(numbers_id)) # 1
Она возвращает единицу, поскольку на список ссылается только переменная чисел.
Это присваивает переменную чисел новой переменной:
ranks = numbers
Количество ссылок на список теперь должно быть равно двум, поскольку на него ссылаются как переменные чисел, так и ранги:
print(ref_count(numbers_id)) # 2
Если вы присвоите переменную Ranks None, количество ссылок в списке уменьшится до одного:
ranks = None print(ref_count(numbers_id)) # 1
А если вы присвоите переменную Numbers None, количество ссылок в списке будет равно нулю:
numbers = None print(ref_count(numbers_id)) # 0
Все это вместе:
import ctypes def ref_count(address): return ctypes.c_long.from_address(address).value numbers = [1, 2, 3] numbers_id = id(numbers) print(ref_count(numbers_id)) # 1 ranks = numbers print(ref_count(numbers_id)) # 2 ranks = None print(ref_count(numbers_id)) # 1 numbers = None print(ref_count(numbers_id)) # 0