3

I know the is operator in Python has an unexpected behavior on immutable objects like integers and strings. See "is" operator behaves unexpectedly with integers

>>> a = 0
>>> b = 0
>>> a is b
True        # Unexpected, we assigned b independently from a

When it comes to mutable objects, are we guaranteed that two variables expected (as written in the code) to reference two distinct objects (with equal value), will not be internally bound to the same object ? (Until we mutate one of the two variables, then of course the references will differ.)

>>> a = [0]
>>> b = [0]
>>> a is b
# Is False guaranteed ?

Put in other words, if somewhere x is y returns True (x and y being mutable objects), are we guaranteed that mutating x will mutate y as well ?

Hibwen
  • 75
  • 4
  • Does this answer your question? [When, if ever, to use the 'is' keyword in Python?](https://stackoverflow.com/questions/22885931/when-if-ever-to-use-the-is-keyword-in-python) – ChrisGPT was on strike Aug 01 '20 at 16:29
  • 3
    Yes, `a is b` will always be false in your second example. Also `is` is a reliable test for reference equality for immutablele objects — the surprising behavior comes from the fact that python reuses objects sometimes when they are immutable. You just can't count on that happening. – Mark Aug 01 '20 at 16:29
  • Check this https://stackoverflow.com/questions/15171695/whats-with-the-integer-cache-maintained-by-the-interpreter – Alex Aug 01 '20 at 16:31
  • `is` should only ever be used for identity. If the semantics of your code are asking about identity, go nuts. For any other use cases, don't use it. – ChrisGPT was on strike Aug 01 '20 at 16:33

1 Answers1

3

So long as you think some "is" behavior is "unexpected", your mental model falls short of reality ;-)

Your question is really about when Python guarantees to create a new object. And when it doesn't. For mutable objects, yes, a constructor (including a literal) yielding a mutable object always creates a new object. That's why:

>>> a = [0]
>>> b = [0]
>>> a is b

is always False. Python could have said that it's undefined whether each instance of [0] creates a new object, but it doesn't: it guarantees each instance always creates a new object. is behavior is a consequence of that, not a driver of that.

Similarly,

>>> a = set()
>>> b = set()
>>> a is b
False

is also guaranteed. Because set() returns a mutable object, it always guarantees to create a new such object.

But for immutable objects, it's not defined. For example, the result of this is not defined:

>>> a = frozenset()
>>> b = frozenset()
>>> a is b

frozenset() - like integer literals - returns an immutable object, and it's up to the implementation whether to return a new object or reuse an existing one. In this specific example, a is b is True, because the implementation du jour happens to reuse an empty frozenset. But, e.g., it just so happens that

>>> a = frozenset([3])
>>> b = frozenset([3])
>>> a is b
False

today. It could just as well return True tomorrow (although that's unlikely - while an empty frozenset is an easy-to-detect special case, it would be expensive to ensure uniqueness across all frozenset objects).

Tim Peters
  • 67,464
  • 13
  • 126
  • 132