3
>>> a_string = "this is a string"
>>> a_method = a_string.upper
>>> a_string.upper()
'THIS IS A STRING'
>>> a_method()
'THIS IS A STRING'
>>> a_string.upper
<built-in method upper of str object at 0x7fb01c024760>
>>> a_method
<built-in method upper of str object at 0x7fb01c024760>
>>> repr(a_string.upper) == repr(a_method)
True
>>> a_method is a_string.upper
False
>>> id(a_string.upper)
140394361523072
>>> id(a_method)
140394360882896

The memory addresses in the reprs are identical (0x7fb01c024760) yet they compare false for identity and the results of the id function are different. What is happening here? I thought that when you make an assignment it just makes the new identifier reference the same piece of memory instead of copying.

Version information:

Python 3.9.6 (default, Jun 30 2021, 10:22:16) 
[GCC 11.1.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

This is CPython, if it matters.

dain
  • 672
  • 1
  • 7
  • 22
  • 1
    [This post](https://stackoverflow.com/a/13348193/16757174) seems to answer your question – kcsquared Sep 09 '21 at 13:48
  • 1
    @kcsquared kind of, though I'm still a bit confused as to why the reprs come out the same yet the `id`s don't. – dain Sep 09 '21 at 13:56
  • A subtle difference from what the duplicate describes is that `str.upper` is not an instance of `function`, but of `method_descriptor`. But `method_descriptor` (as the name implies) implements the descriptor protocol just as `function` does. The accepted answer also mentions unbound methods, which are replaced by ordinary functions in Python 3. – chepner Sep 09 '21 at 13:56
  • @dain They are *equivalent* objects, but not *identical* objects. It's like if you defined two functions `def f(): return 1` and `def g(): return 1`. – chepner Sep 09 '21 at 13:58
  • 1
    The memory address in the `__repr__` of `a_method` doesn't in fact refer to the memory address of the method. It in fact refers to the memory address of the *string to which the method is bound*. You can verify this by trying out `object.__repr__(a_string)` in the interactive terminal, which will show you the memory address of `a_string`. (I agree that the `__repr__` of `a_method` is ambiguously worded in this respect.) – Alex Waygood Sep 09 '21 at 13:59
  • @chepner yeah I get that, but in your example `repr(f)` and `repr(g)` are different. why are the reprs the same in my example? – dain Sep 09 '21 at 14:00
  • 1
    @AlexWaygood ah thank you, so I should parse the sentence as "built-in method upper of (str object at 0x7fb01c024760)" rather than "(built-in method upper of str object) at 0x7fb01c024760" ? – dain Sep 09 '21 at 14:01
  • 1
    @dain, yes, precisely. – Alex Waygood Sep 09 '21 at 14:02
  • I would presume that the reason why the method doesn't have its *own* memory address in its own `__repr__` is because, as the answer linked to above points out, the object returned by `a_string.upper` is different every time, so it doesn't really *have* a permanent memory address. But that's just speculation on my part. – Alex Waygood Sep 09 '21 at 14:08
  • The repr just doesn't report its own address, only the address of the underlying string object. Really, you should never worry about *what* `repr` reports; it's not meant as an authoritative source of information. What matters is that `id` reports different values. – chepner Sep 09 '21 at 15:48

0 Answers0