6
float(1.0) is float(1.0) #True
float(1) is float(1) #False

I've isolated the float() oddity here to the object creation order, because

x1 = float(1)
x2 = float(1)
x1 is x2 #False
id(x1) == id(x2) #False
y1 = float(1.0)
y2 = float(1.0)
y1 is y2 #True
id(y1) == id(y2) #True

Note: I've done checks on the precision of the floats and it is not the reason why this is happening.

I wish to understand why and how is Python deciding to create float objects. Why is float(1.0) pointing to the same object while float(1) points to 2 different objects when either are created twice?

Also, for further reference:

float(1) is float(1) #False
id(float(1)) == id(float(1)) #True
float(1.0) is float(1.0) #True
id(float(1.0)) == id(float(1.0)) #True
petezurich
  • 9,280
  • 9
  • 43
  • 57
ycx
  • 3,155
  • 3
  • 14
  • 26

2 Answers2

7
>>> float(1.0) is float(1.0)
True

That's because float returns the object itself because it's already a float (same for strings BTW Should I avoid converting to a string if a value is already a string?).

Checking the source code to confirm (with added comments):

static PyObject *
float_float(PyObject *v)
{
    if (PyFloat_CheckExact(v))   // if v is already a float, just increase reference and return the same object
        Py_INCREF(v);
    else
        // else create a new float object using the input value
        v = PyFloat_FromDouble(((PyFloatObject *)v)->ob_fval);
    return v;
}

And the reference on literal 1.0 is probably shared when compiled (that's implementation defined, and that's the only explanation I can think of, Dunes answer explains it much better), so it's the same as 1.0 is 1.0.

>>> float(1) is float(1)
False

Python has to create floating point objects for each side, so it's different. There isn't any floating point interning like integers.

And last fun part:

>>> id(float(1)) == id(float(2))
True

because the float object is garbage collected after id has been called, so id is reused, even if the literal values are different as the above example shows (as exposed in Unnamed Python objects have the same id or Why is the id of a Python class not unique when called quickly?).

Jean-François Fabre
  • 137,073
  • 23
  • 153
  • 219
  • Can you address the last codeblock in further reference? If what you said is true, why doesn't `id(float(1)) == id(float(1))` behave the same way as `float(1) is float(1)` when called? How exactly is it being implemented and do I have to go to the CPython level to see it or which part of `help(float)` can address this? – ycx Dec 12 '18 at 18:59
  • 1
    @ycx Intuitively float(1.0) will not create a new object but refer to the original object containing the value of 1.0. So every time you call float(with_float_value) it is referring to the previous object rather than creating the new object. But `float(1)` will have to create a new object each time so its id will be different everytime. – mad_ Dec 12 '18 at 19:02
  • @Jean-François Fabre -I saw your edits and they have answered my question. Thanks! – ycx Dec 12 '18 at 19:03
  • it was a very interesting question! – Jean-François Fabre Dec 12 '18 at 19:05
  • @Jean-François Fabre However, is there anywhere in the documentation of Python or help(float) that addresses the part where it takes the float as it is and does a conversion for int? – ycx Dec 12 '18 at 19:08
  • 1
    Concerning your last point: to be exact, not the object is reused, but the 2nd object is created at the same address as the 1st object. – glglgl Dec 12 '18 at 19:22
  • 1
    @glglgl not saying otherwise, my answer says the _id_ is reused, not the _object_ – Jean-François Fabre Dec 12 '18 at 19:30
  • 1
    @ycx checked the source code and edited my answer. The last point that I have to guess is the shared references of float literals, I don't have a reference (haha) for that. – Jean-François Fabre Dec 12 '18 at 19:45
  • 2
    Worth noting that the reuse of the `id` for two consecutive objects is a quirk of CPython. It's practically guaranteed to happen in CPython, but practically guaranteed to *not* happen in every other Python implementation I know of. – Daniel Pryden Dec 12 '18 at 19:45
  • @DanielPryden I'll link to this other SO question: https://stackoverflow.com/questions/24802740/unnamed-python-objects-have-the-same-id – Jean-François Fabre Dec 12 '18 at 20:09
  • 1
    @Jean-FrançoisFabre I would actually recommend linking to this one instead: [Why is the id of a Python class not unique when called quickly?](https://stackoverflow.com/questions/20753364/why-is-the-id-of-a-python-class-not-unique-when-called-quickly) – Daniel Pryden Dec 12 '18 at 20:36
3

1.0 is literal syntax for a float object, and so the interpreter has to create a float object that it can pass to float. Since floats are immutable, the float function can just return the object unchanged. On the other hand 1 is literal syntax for an integer. As such, the float function must create a new float object. Within the same block of code the interpreter sometimes (not always) is able to identify that two literals for immutable objects are identical then it is able to cache that object and reuse for other references. This is an internal memory optimisation and should not be relied upon.

Thus:

def f():
    x = 1.0
    y = float(1.0)
    z = float(x)
    assert x is y # x and y are the same object
    assert x is z # z is also the same as both x and y
f()

But:

def f():
    return 1.0

def g():
    return 1.0

assert f() is not g() # The interpreter was not able to detect it could reuse the same object

Long story short, sometimes numbers in python that are equal might also be the exact same object, but that this is not guaranteed.

Dunes
  • 37,291
  • 7
  • 81
  • 97
  • Appreciate the detailed explanation about immutability and literals. Would you also mind explaining the bit on `float(1) is float(1) #False; id(float(1)) == id(float(1)) #True`? Is it as what Jean-François Fabre said in his answer about the garbage collection or do you have a better explanation? – ycx Dec 12 '18 at 19:17
  • @ycx I have added 2 links about that in my answer. Both answers complete themselves beautifully if you ask me. The part about python reusing the same object only in the same expression was cool. – Jean-François Fabre Dec 12 '18 at 20:40