0

Could some explain this in detail:

print(float(5.3) is float(5.3))#True
print(float(5) is float(5))#False
print(int(5) is int(5))#True

The first one returns False and the second one returns True. So I would really be grateful if you guys could me what is happening behind the scenes. Thanks in Advance:)

EDIT: Appreciate everyone's comments but I really do understand that the IDs are different in the 2nd case and the same in the 1st case. And obviously, it can be found out using id(). But what I exactly want to know is whyyy the ID's are different?

Hrishi
  • 41
  • 9
  • I guess a more correct way of checking if objects are equal: `print(id(float(5)) == id(float(5)))`, which gives `True` – Alexey S. Larionov Aug 31 '21 at 13:10
  • `id(x) == id(y)` should always be equivalent to `x is y`. The results might vary across different versions or implementations of Python, though. – kaya3 Aug 31 '21 at 13:18
  • 2
    You aren't testing what you think you are here. The two literals on the same line do not exist at the same time (they are eligible for garbage collection as soon as `float()` or `int()` is called), so you cannot distinguish between "two references to the same object" and "two separately-created objects that happened to get allocated at the same memory address". – jasonharper Aug 31 '21 at 13:20
  • Yes, good point. – kaya3 Aug 31 '21 at 13:22
  • @jasonharper Thank you, Sir. Your answer looks very convincing. If you don't mind, I would be grateful if you could explain it to me in detail:). Like what you meant by garbage collection in this context. Thanks – Hrishi Aug 31 '21 at 13:42
  • @kaya3 Yes true. I was more interested on "why the ID's" it is different? My apology for not clear mentioning it in the question. I have updated it:) – Hrishi Aug 31 '21 at 13:44

2 Answers2

2

To avoid the issue of whether two occurrences of the same literal are the same object (that's an implementation detail), let's give our values names so that the same name always refers to the same object.

>>> x = 5.3
>>> y = 5

We can see that in fact, when you call the float or int constructors on values which already are floats or ints respectively, they do not construct new objects, instead they return the original objects:

>>> x is float(x)
True
>>> y is int(y)
True

So of course float(x) is float(x) will be True, because that's the same as writing x is x. Likewise for int(y) is int(y). The docs say "For a general Python object x, float(x) delegates to x.__float__()" and "If x defines __int__(), int(x) returns x.__int__()", and it makes sense that float.__float__ and int.__int__ both just return the object itself.

However, when you call the float constructor multiple times on the same int value, you are constructing a new object each time:

>>> y1 = float(y)
>>> y2 = float(y)
>>> y1 is y2
False

But this is an implementation detail. The interpreter would be permitted to use a cache to return the same float object instead of constructing multiples, and in the int case this is what it actually does, at least for small int values in CPython.

>>> x1 = int(x)
>>> x2 = int(x)
>>> x1 is x2
True
kaya3
  • 47,440
  • 4
  • 68
  • 97
  • Thank you so much!!! I somewhat got a good understanding of your answer:) I would really be grateful if you could explain, if you are free, a little more in detail on the last few lines which you wrote. About that cache. – Hrishi Aug 31 '21 at 13:49
  • 1
    If you follow the link to [the other Q&A](https://stackoverflow.com/questions/306313/is-operator-behaves-unexpectedly-with-integers), you can find an explanation of how `int` objects are cached in CPython. – kaya3 Aug 31 '21 at 13:51
  • Oh okay. Thanks:) – Hrishi Aug 31 '21 at 13:51
1

Python caches small integer objects, and your example demonstrates that when this happens is not always obvious. As such you should us id() to compare identities.

You can find other questions on SO that give more details about this cacheing such as: What's with the integer cache maintained by the interpreter?

Ari Cooper-Davis
  • 3,374
  • 3
  • 26
  • 43