1

The following pattern emerges when calling for the identity of mutable objects with equal values. As you can see, the value returned by id(mutObj) is not entirely independent of the previous return values of id(mutObj), but the pattern is erratic.

This behaviour is probably inconsequential for Python code itself, nonetheless I'd be very interested to learn about the underlying mechanism!

>>> id([1,2])
6706248
>>> id([1,2])
59597256
>>> id([1,2])
56866632
>>> id([1,2])
56866632
>>> id([1,2])
56881992
>>> id([1,2])
56881992
>>> id([1,2])
56879624
>>> id([1,2])
56867784
>>> id([1,2])
56867784
>>> id([1,2])
56879624

Python 3.4.3 [MSC v.1600 64 bit (AMD64)] on win32

In particular, note that the last value is equal to the one returned three calls earlier.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
Pieter Kockx
  • 179
  • 5
  • 4
    As soon as an object is destroyed, its ID is up for reuse. An object that is not referenced anywhere can be destroyed. There is no misbehaviour here, just recycling taking place. – Ulrich Eckhardt Dec 30 '15 at 09:06
  • 4
    Two objects with non-overlapping lifetimes MAY have the same id() value. https://docs.python.org/2/library/functions.html#id. Probably done to fasten things up ? – Kiran Ruth R Dec 30 '15 at 09:11
  • 5
    mutability has nothing to do with this, you can get similar results with `id((1,2))`. Every object your are instantiating is immediately eligible to be collected as garbage. You are running this in the REPL which surely does its own allocations in processing your input. A few entries in the REPL doesn't really demonstrate a 'pattern', and as @KiranRuthR points out, there is bupkis preventing python from reusing an id of objects of non-overlapping lifetimes. Try it with different objects, you will likely get similar results. – pvg Dec 30 '15 at 09:16
  • 1
    If you want to dig into this, the `dis` module can show you what Python bytecode is executed for a given function, and you can see the bytecode interpreter loop in [`Python/ceval.c`](https://hg.python.org/cpython/file/3.4/Python/ceval.c). A list literal uses the `BUILD_LIST` opcode, which uses [`PyList_New`](https://hg.python.org/cpython/file/3.4/Objects/listobject.c#l129) to get the list object, and that uses a free list to reuse old list objects. I'm not quite sure what intervening allocations could be causing the erratic behavior you're seeing - it's usually way more consistent. – user2357112 Dec 30 '15 at 09:24
  • [For reference, if you just take `id([1, 2])` over and over, it usually looks like this.](http://ideone.com/JIvwpI) – user2357112 Dec 30 '15 at 09:29
  • Using dynamic RAM and waiting static RAM actions ? Allocate every data address inside an ap , you will be gained a nonrandom id table. But who can clear address ? How to relocate all data address? Auto garbage control, a second before free ram starting at 0x1200 but now 0x11ED because an app remove owned data on RAM. – dsgdfg Dec 30 '15 at 11:59

2 Answers2

2

Immediate re-use of id

Python use the same id for all objects you create:

>>> for x in range(10):
        print(id([1, 2]))
4479841736
4479841736
4479841736
4479841736
4479841736
4479841736
4479841736
4479841736
4479841736
4479841736

Because there is no reference to the created list, it will be garbage collected immediately and the id will be re-used.

Using the same name repeatedly

Assigning the list to a name obj, will keep the lists alive until a new list is assigned to the same name:

>>> for x in range(10):
        obj = [1, 2]
        print(id(obj))
4486925128
4486925192
4486925128
4486925192
4486925128
4486925192
4486925128
4486925192
4486925128
4486925192

In the second iteration obj exists and obj = [1, 2] creates new lists first before assigning to obj. Once assigned, the old list will be garbage collected and the id becomes available again for re-use. Repeat this for each following iteration.

Keeping a reference

When you keep a reference in a list to all created lists:

>>> objs = []
>>> for x in range(10):
        obj = [1, 2]
        print(id(obj))
        objs.append(obj)
4480011848
4483963144
4486916488
4486914376
4486914568
4486916616
4486914824
4486915016
4486915272
4486915464

Python has to use different ids for all of them.

Interpreter effects

Working at the standard interactive prompt (Python 3.5.1), I can produce this:

>>> id([1, 2])
4330529608
>>> id([1, 2])
4330529608
>>> id([1, 2])
4330529608
>>> id([1, 2])
4330529608
>>> id([1, 2])
4330529608
>>> id([1, 2])
4330529608

But working in IPython or an IPython Notebook, you get this:

In [1]: id([1, 2])
Out[1]: 4359684552

In [2]: id([1, 2])
Out[2]: 4379935816

In [3]: id([1, 2])
Out[3]: 4373482696

In [4]: id([1, 2])
Out[4]: 4359674248

In [5]: id([1, 2])
Out[5]: 4373482696

This due to the fact that IPython is doing some more work in background and creates objects that re-use the freed id before you can create your new object.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
1

Quote from the docs (emphasis mine):

id(object)

Return the “identity” of an object. This is an integer which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

CPython implementation detail: This is the address of the object in memory.

Basically, when you write id([1, 2]), you create a list, check its id, and throw it away, which makes it eligible for garbage collection. In CPython, due to garbage collection by reference counting, it's then deleted, and nothing prohibits creating another object in the exactly same place in memory, thus, with the same id.

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52