sys.getrefcount() prints one more than the expected number of references to an object?

Question:

I would like to know why this code prints 4 instead of 3. Where is the fourth reference?

import sys

def f(a):
    print(sys.getrefcount(a))

a = [1, 2, 3]
f(a)
Asked By: ThunderPhoenix

||

Answers:

We can make sense of this in steps:

import sys

print(sys.getrefcount([1, 2, 3]))
# output: 1
import sys

a = [1, 2, 3]
print(sys.getrefcount(a))
# output: 2
import sys

def f(a):
    print(sys.getrefcount(a))

f([1, 2, 3])
# output: 3
import sys

def f(a):
    print(sys.getrefcount(a))

a = [1, 2, 3]
f(a)
# output: 4

So to reiterate:

  • First reference is the global variable a
  • Second reference is the argument passed to the f function
  • Third reference is the parameter that the f takes
  • Fourth reference is the argument passed to the getrefcount function

The reason for no fifth reference, from the parameter getrefcount takes, is that it’s implemented in C, and those don’t increase the reference count in the same way. The first example proves this, since the count is only 1 in that.

Answered By: ruohola

I wanted to explain it a bit more. First I’m gonna use another method to get the number of references using it’s id:

import ctypes

def get_ref_count(id_: int):
    return ctypes.c_long.from_address(id_).value

a = [1, 2, 3]
a_id = id(a)
print(get_ref_count(a_id))   # 1

by just passing a as the argument of the get_ref_count, the count goes to 3.

import ctypes

def get_ref_count(id_: int, un_used):   #  <----------
    return ctypes.c_long.from_address(id_).value

a = [1, 2, 3]
a_id = id(a)
print(get_ref_count(a_id, a))   # 3

See, I didn’t actually do anything with a. But what happens?

When you call a funcion fn(a), Python will load the fn and a and puts them on the stack. this is LOAD_NAME instruction. This instruction calls Py_INCREF() which increments the number of references by one. Then in order to call that function, it creates a Frame object and pass that reference as the frame’s local attribute (Function’s parameters are local variables of that function):

from inspect import currentframe

def f(var):
    print(currentframe().f_locals)   # {'var': [1, 2, 3]}

a = [1, 2, 3]
f(a)

f_locals is exactly functions’s local scope returned by locals().

So that’s why it incremented by 2.

Answered By: S.B
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.