fonini fonini - 5 months ago 6x
Python Question

In Python, when are two objects the same?

It seems that

2 is 2
3 is 3
will always be true in python, and in general, any reference to an integer is the same as any other reference to the same integer. The same happens to
None is None
). I know that this does not happen to user-defined types, or mutable types. But it sometimes fails on immutable types too:

>>> () is ()
>>> (2,) is (2,)

That is: two independent constructions of the empty tuple yield references to the same object in memory, but two independent constructions of identical one-(immutable-)element tuples end up creating two identical objects. I tested, and
s work in a manner similar to tuples.

What determines if an object will be duplicated in memory or will have a single instance with lots of references? Does
depend on whether the object is "atomic" in some sense? Does it vary according to implementation?


Python has some types that it guarantees will only have one instance. Examples of this are None, NotImplemented, Ellipsis. These are (by definition) singletons and so things like None is None are guaranteed to return True because there is no way to create a new instance of NoneType.

It also supplies a few doubletons 1 True, False 2 -- All references to True point to the same object. Again, this is because there is no way to create a new instance of bool.

The above things are all guaranteed by the python language. However, as you have noticed, there are some types (all immutable) that store some instances for reuse. This is allowed by the language, but different implementations may choose to use this allowance or not -- depending on their optimization strategies. Some examples that fall into this category are small integers (-5 -> 255), the empty tuple and empty frozenset.

Note that is can be used to compare things that aren't singletons. One common use is to create a sentinel value:

sentinel = object()
item = next(iterable, sentinel)
if items is sentinel:
   # iterable exhausted.


_sentinel = object()
def function(a, b, none_is_ok_value_here=_sentinel):
    if none_is_ok_value_here is sentinel:
        # Treat the function as if `none_is_ok_value_here` was not provided.

The moral of this story is to always say what you mean. If you want to check if a value is another value, then use the is operator. If you want to check if a value is equal to another value (but possibly distinct), then use ==.

1I might have just made up that word ... But hopefully you get the idea...
2Under normal circumstances, you don't need do check if the object is a reference to True -- Usually you just care if the object is "truthy" -- e.g. if if some_instance: ... will execute the branch. But, I put that in here just for completeness.