The id
is defined to be unique for an object across its lifetime. That is, two separate objects existing at the same time cannot have the same id
. However, two separate objects existing at different time as well as objects not required to be separate may have the same id
.
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.
Thus, one has to be mindful of two things when reasoning about id
: When must the lifetime of objects overlap, and when must two objects be separate.
When the objects whose id
we look at are created only for the id
:
>>> # / / first a[:]
>>> # v v v v second a[:]
>>> id(a[:]) == id(a[:])
True
then the object are not required to exist at the same time. Each id(a[:])
expression can create the slice, get its id
and then discard the slice before the equality between the id
s is ever checked. This means both slice can have the same id
as they never exist at the same time.
In contrast, when a slice is assigned to a variable it has to exist at least as long as the variable. Thus, when we check the id
of an object via a variable
>>> b = a[:] # < ------------- first a[:]
>>> id(b) == id(a[:]) # < second a[:] |
False
>>> b # < ----------------/
…
its lifetime overlaps with that of the temporary slice. This means both slices must not have the same id
as they never exist at the same time…
… iff slicing must create separate objects.
When comparing the behaviour of list
and str
, the key difference is that the latter does not have behaviour depending on its identity – roughly, this corresponds to mutable and immutable types.
When working with list
s, identity is important because we can mutate a specific object. Even if two objects have the same initial value, mutation has a different effect:
>>> a, b = [1, 2, 3], [1, 2, 3]
>>> c = a # a, b, c have same value
>>> c += [4] # changing c has different effect on a and b
>>> a == b
False
When working with str
, identity is irrelevant because we cannot mutate a specific object. If two objects have the same initial value, immutability guarantees they will always have the same value:
>>> a, b = "123", "123"
>>> c = a # a, b, c have same value
>>> c += "4" # changing c has *no* effect on a and b
>>> a == b
True
As a result, slicing a mutable list
to a new list
must always create a new object. Otherwise, mutating the slice would have unreliable behaviour.
In contrast, slicing an immutable str
to a new str
may create a new object. Even if it always provides the same object the behaviour is the same.
As a result of how id
is defined – in respect to lifetime and separation – a Python implementation must use separate id
s in specific cases but may use separate id
s in all other cases.
In specific, a Python implementation is free to re-use id
s if objects don't exist at the same time and is free to share id
s if behaviour does not depend on identity.