3

I have the following code which defines a class and two class objects:

class A():
    def instance_method(self):
        pass

a1 = A()
a2 = A()

a1 and a2 does not appear to share the same instance method, which makes sense to me.

assert a1.instance_method is not a2.instance_method

However, when I checked the ID of the two methods, they appear identical.

assert id(a1.instance_method) == id(a2.instance_method)

So I am a bit confused about whether or not instances share an instance method. Can anyone shed a light on what's going on here in memory?

wjandrea
  • 28,235
  • 9
  • 60
  • 81
l001d
  • 723
  • 9
  • 15
  • 1
    Does this answer your question? [Why is a method not identical to itself?](https://stackoverflow.com/questions/4639908/why-is-a-method-not-identical-to-itself) – Eugene Primako Feb 26 '21 at 13:13
  • Thanks but no it doesn’t. Read that before firing this q. After a bit further research I find something called descriptor may be at play here; and I am trying to make some sense out of that. – l001d Feb 26 '21 at 21:58
  • In your check, `a1.instance_method` and `a2.instance_method` did not exist at the same time (they were eligible for garbage collection as soon as their `id()` had been obtained). It's therefore possible that the second one was allocated at the same address as the first one; due to the way CPython's memory management works, it's actually quite likely for a new object to reuse the memory of the most recently deleted object of the same type. On the other hand, this is not at all guaranteed. Basically, your test proved nothing whatsoever. – jasonharper Mar 01 '23 at 17:13
  • @jasonharper The "allocated" part of that is what's not obvious. Naively, you'd think the `instance_method` attribute would refer to the same function object, but then you remember `self` needs to get passed in, which is done with a descriptor. That also means that even methods of the same object aren't identical: `a1.instance_method is not a1.instance_method` but every time I tested, `id(a1.instance_method) == id(a1.instance_method)`. – wjandrea Mar 01 '23 at 17:39
  • @jasonharper Could you post an answer? I would, but I bet you're more familiar with garbage collection than me. – wjandrea Mar 01 '23 at 17:41
  • @wjandrea, this is surely a duplicate, a hundred times over. – jasonharper Mar 01 '23 at 17:45
  • 1
    Does this answer your question? [Why don't methods have reference equality?](https://stackoverflow.com/questions/15977808/why-dont-methods-have-reference-equality) – wjandrea Mar 01 '23 at 17:57
  • @jasonharper Found it! VTC if you can please – wjandrea Mar 01 '23 at 17:58
  • @jasonharper Also check out [this question](https://stackoverflow.com/q/75604466/4518341) too; I think it's also a duplicate. – wjandrea Mar 01 '23 at 18:01
  • I tested the "quite likely" bit with ``` for i in range(1000000): a3 = A() assert id(a1.instance_method) == id(a2.instance_method) ``` but never failed once. So either I am extremely lucky, or something else is at play? initially I looped without a3 but added it just hoping to make it less likely for the same address to be re-allocated. – l001d Mar 01 '23 at 21:49
  • @l001d See the docs for [`id()`](https://docs.python.org/3/library/functions.html#id): *"Two objects with non-overlapping lifetimes may have the same `id()` value."* So what you're seeing is undefined behaviour. I'm not familiar with how CPython does memory management, but hypothetically, maybe it only allocates one memory slot to hold a reference to `a1.instance_method` while getting its ID, then uses the *same* slot to hold a reference to `a2.instance_method` while getting *its* ID. – wjandrea Mar 02 '23 at 01:10

0 Answers0