-1

I know the is operator compares the id of the two, not the value but why is id([1][::-1])==id([1]) returning True while [1][::-1] is [1] is returning False?

id([1][::-1]) ==id([1]) #True
[1][::-1] is [1] #Fasle

id([1]) ==id([1][::-1])#Fasle
[1] is [1][::-1]#False
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
None
  • 23
  • 2
  • 1
    don't use `is` here. Use `==`https://stackoverflow.com/questions/132988/is-there-a-difference-between-and-is – David Erickson Oct 23 '20 at 03:46
  • I just want to know why [1][::-1]==[1] and id([1][::-1]) ==id([1]) is returning True while [1][::-1] is [1] is still returning False? – None Oct 23 '20 at 03:52
  • 1
    This is not a duplicate of [Is there a difference between “==” and “is”?](https://stackoverflow.com/q/132988/364696); this question is very specifically asking about why the `id`s appear to be the same, but the identity test doesn't agree. – ShadowRanger Oct 23 '20 at 03:55
  • What are the contents of `id`? – Sabito stands with Ukraine Oct 23 '20 at 03:59
  • @Yatin: `id` is a built-in function, you can read the docs for details; on CPython, it returns the memory address of the object. – ShadowRanger Oct 23 '20 at 04:04

1 Answers1

2

id([1][::-1]) ==id([1]) being True is a quirk of CPython using a free list for lists being implemented as a stack. The first id([1][::-1]) is completely evaluated, and the list it produced is freed by the time it finishes. When [1] allocates space for the new list, it's given the same memory at the same address, which (in CPython) is equivalent to its id.

ids are only guaranteed to be unique for as long as the object exists; since the original list is gone before you make the new list, no guarantees are violated by both of them having the same ids at slightly different points in time.

The order of the test matters because id itself is pulling from the set of recently freed blocks in the small object allocator. The ordering here is:

  1. Original [1] is allocated at address A
  2. Result of [::-1] is allocated at address B
  3. Immediately thereafter, [1] allocation A is returned to list's free list
  4. id returns an int describing address B
  5. Result of [::-1], B, is returned to free list (ahead of A)
  6. Second [1] is allocated, getting B, the memory previously used by [::-1]
  7. id is called, accurately producing the same id as before (because the new [1] was given the memory recently freed from the list [::-1] produced), because both lists were using address B

That's actually more complicated than it needs to be; id([1]) == id([1]) would end up with the same result as well (it would just leave one fewer lists on the list free list).

When you do it the other way around, this happens:

  1. [1] is allocated at address A
  2. id returns a new int describing address A
  3. [1] is freed and address A is returned to the pool
  4. The next [1] is allocated, getting A, the same memory as the first [1]
  5. [::-1] is applied to it, getting a new list from fresh memory B; [1] is then thrown away
  6. id is called on the result of [::-1] getting an int describing address B; the ids for addresses A and B don't match

If you created and stored both lists, then compared the ids of the still existing lists to one another, they would be unique because these memory reuse shenanigans wouldn't be able to occur.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271