`getrefcount` different for lists and tuples!

Hi All,

I’m trying to write a package that checks calls for parallel safety by looking at the ref count to make sure that an object isn’t on two separate threads. I’m seeing something curious:

>>> import sys
>>> sys.getrefcount([1,2,3])
1
>>> sys.getrefcount((1,2,3))
2

Note 1st is a list, 2nd a tuple. Why are the ref counts different? I’m using 3.14.

Since I’m using 3.14 I wondered if it was a compiler optimization that @mpage explained in a previous related post, Strange ref count , was involved, so I also looked at the bytecode:

>>> import dis
>>> code_list = compile('sys.getrefcount([1,2,3])', '<string>', 'eval')
>>> eval(code_list)
1
>>> dis.dis(code_list)
  0           RESUME                   0

  1           LOAD_NAME                0 (sys)
              LOAD_ATTR                3 (getrefcount + NULL|self)
              BUILD_LIST               0
              LOAD_CONST               1 ((1, 2, 3))
              LIST_EXTEND              1
              CALL                     1
              RETURN_VALUE
>>> code_tuple = compile('sys.getrefcount((1,2,3))', '<string>', 'eval')
>>> eval(code_tuple)
2
>>> dis.dis(code_tuple)
  0           RESUME                   0

  1           LOAD_NAME                0 (sys)
              LOAD_ATTR                3 (getrefcount + NULL|self)
              LOAD_CONST               1 ((1, 2, 3))
              CALL                     1
              RETURN_VALUE

But can’t see anything different, apart from list to tuple change.

Anyone know what’s going on?

Thanks in advance. Howard.

Notice the LOAD_CONST instruction. Because this tuple is an entirely constant value, there’s no need to create a new object for every call. Instead it’s stored in the code_tuple.__code__.co_consts array, then fetched from there. In the list example, you can see the same constant appear there - Python converted your code to an equivalent of [*(1, 2, 3)], so it can have a single extend() operation instead of 3 append()s. Try compiling (1, 2, 3, x, 4, y, 5, 6) or the same as a list, you’ll get different bytecode again.

3 Likes

Thanks @TeamSpen210