1

In Python, the expression (a is b) == ( id(a) == id(b) ) appears to always returns True, where a and b are variables referring to some object, since the id function returns the memory where they are stored and is is used for object identity.

Are there any exceptions?

nnnmmm
  • 7,964
  • 4
  • 22
  • 41

2 Answers2

3
(a is b) == ( id(a) == id(b) )

This expression is always True. There are two possible ways: 1. Both a and b refer to the same object

>>>a='Hello'
>>>b=a
>>> a is b
True
>>> id(a)==id(b)
True
  1. Both 'a' and 'b' refer different objects.
>>>a='hello'
>>>b='world'
>>>a is b
False
>>>id(a)==id(b)
False

id(a)==id(b) does what a is b does.

Now, Incase1 (a is b) == ( id(a) == id(b) ) this is True==True which returns True. In second case (a is b) == ( id(a) == id(b) ) this is False==False which returns True

From Docs:

The operators is and is not test for an object’s identity: x is y is true if and only if x and y are the same object. An Object’s identity is determined using the id() function. x is not y yields the inverse truth value.

Ch3steR
  • 20,090
  • 4
  • 28
  • 58
  • You only demonstrated that it is the case for two strings, not that it is always the case. – nnnmmm Mar 28 '20 at 12:48
  • @nnnmmm Can you show a case where `a is b == (id(a)==id(b))` returns `False`? For which datatype this doesn't hold `True` – Ch3steR Mar 28 '20 at 12:53
  • No, I think it's true that they're always the same, but the answer doesn't explain that. You could link to an authoritative source instead that explains e.g. that the `is` operator is implemented via comparison of object addresses. – nnnmmm Mar 28 '20 at 12:55
  • 1
    @nnnmmm I exactly answered that. `a is b` is `True` only when `a` and `b` are referring to the same object. And `id(a)==id(b)` is True` only when `a` and `b` are referring to the same object. That was the reason for a downvote? I don't understand what more I have to explain. The question was not about how `is` is implemented. – Ch3steR Mar 28 '20 at 12:59
  • 1
    @nnnmmm [docs](https://docs.python.org/3/reference/expressions.html#is-not) Also say the same. Moreover the question about why that expression always evaluates to `True` and not about how `is` or `id` is implemented. I don't really understand the reason you downvoted? ;) – Ch3steR Mar 28 '20 at 13:25
  • 2
    @nnnmmm [`id()` is defined as the "identity" tag of an object](https://docs.python.org/3/library/functions.html#id), and [`is` is an identity check](https://docs.python.org/3/reference/expressions.html#is-not), they match by specification and by definition. – Masklinn Mar 28 '20 at 13:29
  • The answer mentioned that but didn't provide any sources for that assertion. Thanks for editing the answer, removed the downvote. – nnnmmm Mar 28 '20 at 13:57
  • @nnnmmm Always open for suggestions and improvements. We collectively improved the answer for the community. `;)` – Ch3steR Mar 28 '20 at 14:42
1

If you use is only to compare to None, which I believe you should, then the question is irrelevant as None has a well-defined address

>>> id(None)
10306432
>>> id(None)
10306432
>>> a = None
>>> id(a)
10306432

I have yet to encounter a situation where a is b yields a result different from id(a) == id(b), but you still don't want to use is carelessly :

>>> a = 1
>>> b = 1
>>> id(a), id(b), id(1)
(10914496, 10914496, 10914496)

# 1 has a single address, is fails to recognize that a and b were created separately.

>>> a = 500
>>> b = 500
>>> id(a), id(b), id(500)
(140251806972464, 140251806973200, 140251806973744)

# Unlike the previous case, 500 is created multiple times, each with a different address

>>> a, b = 500, 500
>>> id(a), id(b), id(500)
(140251806972336, 140251806972336, 140251806972464)

# Several instances during the same initialization are created with a single address

>>> a, b = 500, 5*100
>>> id(a), id(b), id(500)
(140251806973104, 140251806973200, 140251806971280)

# However it only works if all are created in the same manner...

>>> a, b = 5*100, 5*100
>>> id(a), id(b), id(500)
(140251806971920, 140251806973392, 140251806973104)

# ... and they have to be created explicitely.


>>> a = 500 ; b = 500
>>> id(a), id(b), id(500)
(140251806973104, 140251806973104, 140251806972464)

# Separating with a semicolon does not change this fact...

>>> 500 is 500
True
>>> a is b
True
>>> a = 500
>>> a is 500
False
>>> a = 500 ; a is 500
True

# However being in the same line is not a basis for always having the same address, even if all values were created explicitly :

>>> any([a is 500 for a in [500]])
False

Other objects each have their own erratic behavior :

>>> a = 'hello'
>>> b = 'hello'
>>> id(a), id(b), id('hello')
(140251789045408, 140251789045408, 140251789045408)

>>> a = 'hello' + ' '
>>> b = 'hello' + ' '
>>> id(a), id(b), id('hello' + ' ')
(140251789044344, 140251789012472, 140251789012920)

>>> a = []
>>> b = []
>>> id(a), id(b), id([])
(140251789066632, 140251789069704, 140251789174216)

I would conclude that the behaviors of is and id are too unreliable for (a is b) == (id(a) == id(b)) to be useful even if it were true, at least in the case of immutable objects.

I personally would never think of using is in any case other than

if x is None:
    pass

and I reserve id for only mutable objects or custom classes.

Neven V.
  • 424
  • 5
  • 13