5.13. OOP Object Identity

5.13.1. Rationale

  • = assignment

  • == checks for object equality

  • is checks for object identity

>>> from typing import Optional
>>>
>>>
>>> firstname: str = 'Melissa'
>>> lastname: str = 'Lewis'
>>> age: Optional[int] = None
>>>
>>> age is None
True
>>> age is not None
False

5.13.2. Identity

  • id(obj) -> int

  • id() will change every time you execute script

  • id() returns an integer which is guaranteed to be unique and constant for object during its lifetime

  • Two objects with non-overlapping lifetimes may have the same id() value

  • In CPython it's also the memory address of the corresponding C object

>>> id('Watney')  
4499664048
>>>
>>> hex(id('Watney'))  
'0x10c336cb0'

5.13.3. Identity Check

  • is checks for object identity

  • is compares id() output for both objects

  • CPython: compares the memory address a object resides in

  • Testing strings with is only works when the strings are interned

  • Since Python 3.8 - Compiler produces a SyntaxWarning when identity checks (is and is not) are used with certain types of literals (e.g. str, int). These can often work by accident in CPython, but are not guaranteed by the language spec. The warning advises users to use equality tests (== and !=) instead.

>>> name = None
>>>
>>> name is None
True
>>> name is not None
False

5.13.4. Bool Identity

>>> name = None
>>>
>>> name is None
True
>>> name is False
False
>>> found = True
>>>
>>> found == True
True
>>> found is True
True

5.13.5. String Identity

>>> a = 'Mark Watney'
>>> b = 'Mark Watney'
>>>
>>> a == b
True
>>> a is b
False
>>> 'Mark Watney' is 'Mark Watney'  
<...>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True

5.13.6. Type Identity

>>> name = ...
>>>
>>> type(name) is int
False
>>> type(name) is float
False
>>> type(name) is complex
False
>>> type(name) is bool
False
>>> type(name) is None
False
>>> type(name) is str
False
>>> type(name) is bytes
False
>>> type(name) is list
False
>>> type(name) is tuple
False
>>> type(name) is set
False
>>> type(name) is frozenset
False
>>> type(name) is dict
False

5.13.7. Object Identity

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> astro1 = Astronaut('Jan', 'Twardowski')
>>> astro2 = Astronaut('Jan', 'Twardowski')
>>>
>>> astro1 is astro2
False
>>> id(astro1)  
4421890496
>>> id(astro2)  
4421893328
>>> hex(id(astro1))  
'0x10790b1c0'
>>> hex(id(astro2))  
'0x10790bcd0'
>>> print(astro1)  
<Astronaut object at 0x107905820>
>>> print(astro2)  
<Astronaut object at 0x10790bcd0>

5.13.8. Value Comparison

  • == checks for object equality

>>> 'Mark Watney' == 'Mark Watney'
True
>>> a = 'Mark Watney'
>>> b = 'Mark Watney'
>>>
>>> a == b
True
>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> astro1 = Astronaut('Jan', 'Twardowski')
>>> astro2 = Astronaut('Jan', 'Twardowski')
>>>
>>> astro1 == astro2
False

5.13.9. Compare Value vs. Identity

>>> name = 'Mark Watney'
>>> expected = 'Mark Watney'
>>>
>>> name == expected
True
>>> name is expected
False
>>> name = 'Mark Watney'
>>>
>>> name == 'Mark Watney'
True
>>>
>>> name is 'Mark Watney'  
<...>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
False

5.13.10. String Value vs Identity Problem

  • CPython optimization

  • Can be misleading

>>> a = 'Mark Watney'
>>> b = 'Mark Watney'
>>>
>>> a == b
True
>>> a is b
False
>>> a is 'Mark Watney'  
<...>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
False
>>> a = 'Mark'
>>> b = 'Mark'
>>>
>>> a == b
True
>>> a is b
True
>>> a is 'Mark'  
<...>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
True

5.13.11. Use Case - Make Equal

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __eq__(self, other):
...         return self.firstname == other.firstname \
...            and self.lastname == other.lastname
>>>
>>>
>>> a1 = Astronaut('Jan', 'Twardowski')
>>> a2 = Astronaut('Jan', 'Twardowski')
>>>
>>> a1 == a2
True
>>> a1 is a2
False

5.13.12. Use Case - Equal Problem

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __eq__(self, other):
...         return self.firstname == other.firstname \
...            and self.lastname == other.lastname
>>>
>>>
>>> class Cosmonaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> a = Astronaut('Jan', 'Twardowski')
>>> c = Cosmonaut('Jan', 'Twardowski')
>>>
>>> a == c
True
>>> a is c
False

5.13.13. Use Case - Make Unequal

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __eq__(self, other):
...         return self.__class__ is other.__class__ \
...            and self.firstname == other.firstname \
...            and self.lastname == other.lastname
>>>
>>>
>>> class Cosmonaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> a = Astronaut('Jan', 'Twardowski')
>>> c = Cosmonaut('Jan', 'Twardowski')
>>>
>>> a == c
False
>>> a is c
False

5.13.14. Use Case - Overload

>>> class Astronaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __eq__(self, other: Astronaut):
...         return self.firstname == other.firstname \
...            and self.lastname == other.lastname
...
...     def __eq__(self, other: Cosmonaut):
...         return False
>>>
>>>
>>> class Cosmonaut:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname
>>>
>>>
>>> a = Astronaut('Jan', 'Twardowski')
>>> c = Cosmonaut('Jan', 'Twardowski')
>>>
>>> a == c
False
>>> a is c
False

5.13.15. Assignments