There are implicit reference increments everywhere in Python. When you pass a as an argument to a function, it is incref-ed, as getrefcount's own docstring notes:
The count returned is generally one higher than you might expect, because it includes the (temporary) reference as an argument to getrefcount().
The two additional references when you invoke the method are, respectively:
- The reference held by the name
self within the method
- The implicit reference created when it makes a bound method object specific to that instance when you do
a.print_ref_count (even before actually calling it); type(a.print_ref_count) is a temporary bound method, which must include references to both the instance and the method itself (you could do to_print = a.print_ref_count, then del a, and to_print must still work even though a is no longer referencing the instance)
Getting hung up on the reference counts is not very helpful though; it's an implementation detail of Python (CPython specifically; other Python interpreters can and do use non-reference counting garbage collection), and as you can see, many implicit references are created that may not actually be stored in named variables at all.