Ehsan
Ehsan

Reputation: 318

inconsistency in "is" behavior over immutable objects in python 3.6 and older vs 3.7

I was introducing is operator to my students when I noticed that there is an inconsistency in its behavior between python (v3.6 and older) and (v3.7).

Launch a python shell and run:

5/2 is 2.5

Or:

(1, 2, 3) is (1, 2, 3)

in v3.6.X you get False for both, but in v3.7 they turn out to be True.

My expectation was that the result should be True as I thought immutable numeric objects (or a tuple of them) have just one instance.

It seems that at least my thought was not right in the previous versions of Python.

Does anyone know what changes have been made which explains this new behaviour?

Upvotes: 3

Views: 105

Answers (4)

Slam
Slam

Reputation: 8572

I'm not sure about reasons and source for this, but my guess is that this has something to do with in-line optimizations.

If you'll assign variable for this values, identity check will result in False, same as before.

>>> 5/2 is 2.5
True
>>> a = 5/2
>>> a is 2.5
False

Interesting note on new folding optimisation. As python is "all runtime", there's no way to optimize some things ahead, but it tries hard, parsing as much scope as it can:

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

Upvotes: 5

Kasravnd
Kasravnd

Reputation: 107287

I believe that this behavior is due moving the Constant folding from the peephole optimizer (compile time operation) to the new AST optimizer (run time operation), which as it's also mentioned in https://docs.python.org/3/whatsnew/3.7.html#optimizations is now able to perform optimizations more consistently. (Contributed by Eugene Toder and INADA Naoki in bpo-29469 and bpo-11549.)

Re:

My expectation was that the result should be True as I thought immutable numeric objects (or a tuple of them) have just one instance.

Immutability is not strictly the same as having an unchangeable value. Before you call an object mutable or immutable it's an object and objects in Python are created at run time. So there's no reason to connect mutability to object creation and identity. There are, however, some exceptions like this one or small object interning in both previous and current versions which, mostly for the sake of optimization, this rule (object creation at run time) gets manipulated. Read https://stackoverflow.com/a/38189759/2867928 for more details.

Upvotes: 2

MSeifert
MSeifert

Reputation: 152587

My expectation was that the result should be True as I thought immutable numeric objects (or a tuple of them) have just one instance.

That expectation is questionable - there's no such thing guaranteed by the Python language.

is is a quite tricky operator because you really need to know when it's appropriate to use it.

For example:

>>> 5 / 2 is 2.5
>>> (1, 2, 3) is (1, 2, 3)

These are not appropriate uses of is in the general case. They may be appropriate if you want to check what line/function optimizations (interning) Python is doing but I guess that wasn't the desired use-case here.

is should only be used if you want to compare to constants (that are guaranteed to only have one instance)! The guaranteed built-in constants are:

  • None
  • NotImplemented
  • Ellipsis (also known as ...)
  • True
  • False
  • __debug__

Or your own constant-like instances:

_sentinel = object()

def func(a=_sentinel):
    return a is _sentinel

Or when you explicitly assign variables to a new name:

a = b
a is b  # <- that's expected to be True

Does anyone know what changes have been made which explains this new behaviour?

Probably the peep-hole optimizer now optimizes more cases (tuples and mathematical expressions). For example "AST-level Constant folding" (https://bugs.python.org/issue29469) has been added in CPython 3.7 (I intentionally wrote CPython here because it's nothing that has been added to the Python 3.7 language specification).

Upvotes: 2

Kaiwen Chen
Kaiwen Chen

Reputation: 322

Why should immutable objects that are the same occupy the same instance?

When using is in python, you are essentially asking if a and b occupy the same piece in memory. If you think of a and b as immutable literals, it's not like python has a specific space to save every type of immutable literal. It's pure chance that it returned true in this instance and entirely possible it will return false if you choose a different literal. Take a look at this:

>>> a = "wtf"
>>> b = "wtf"
>>> a is b
True

>>> a = "wtf!"
>>> b = "wtf!"
>>> a is b
False

>>> a, b = "wtf!", "wtf!"
>>> a is b
True

If you want to avoid this, do not use is on things you didn't explicitly save into memory.

Upvotes: 1

Related Questions