3. Object Identity

3.1. id()

  • id() will change every time you execute script

  • An integer which is guaranteed to be unique and constant for this 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

3.2. hash()

  • Return the hash value of the object (if it has one)

  • hash() ->  int

  • They are used to quickly compare dictionary keys during a dictionary lookup

  • set() elements has to be hashable

  • dict() keys has to be hashable

  • User-defined classes have __eq__() and __hash__() methods by default

  • All objects compare unequal (except with themselves)

  • x.__hash__() returns an appropriate value such that x == y implies both that x is y and hash(x) == hash(y)

Listing 380. dict() keys has to be hashable
key = 'my_key'

my_dict = {
    'a': 'key can be str',
    2: 'key can be int',
    3.3: 'key can be float',
    ('a', 2, 3.3): 'key can be tuple',
    key: 'key can be str',
}
Listing 381. set() elements has to be hashable
{1, 1, 2}
# {1, 2}

{1, 1.1, 'a'}
# {1, 1.1, 'a'}

{'a', (1, 2)}
# {'a', (1, 2)}
Listing 382. set() elements has to be hashable
class Astronaut:
    def __init__(self, name):
        self.name = name


jan = Astronaut('Jan Twardowski')
data = {jan, jan}
len(data)
# 1

data = {Astronaut('Jan Twardowski'), Astronaut('Jan Twardowski')}
len(data)
# 2
Listing 383. Generating hash and object comparision
class Astronaut:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __hash__(self, *args, **kwargs):
        """
        __hash__ should return the same value for objects that are equal.
        It also shouldn't change over the lifetime of the object;
        generally you only implement it for immutable objects.
        """
        return hash(self.first_name) + hash(self.last_name)

    def __eq__(self, other):
        if self.first_name == other.first_name and \
                self.last_name == other.last_name:
            return True
        else:
            return False
Listing 384. Generating hash and object comparision. Since Python 3.7 dict has fixed order
class Astronaut:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    def __hash__(self):
        return hash(self.__dict__)

    def __eq__(self, other):
        if self.__dict__ == other.__dict__:
            return True
        else:
            return False

3.3. String interning

  • https://en.wikipedia.org/wiki/String_interning

  • Method of storing only one copy of each distinct string value, which must be immutable

  • many high level languages use it

  • string literals and values are internally kept in a hashed list called 'string intern pool' and those which are identical are rendered as references to the same object

  • possible because str are immutable

  • implementation dependent

  • Jython, IronPython, PyPy and others may intern more aggressively

  • Calling the intern() function on strings will "force" a string to have the same object identity as any equivalent and previously interned string

Listing 385. CPython 3.7.4
('a' * 4096) is ('a' * 4096)
# True

('a' * 4097) is ('a' * 4097)
# False

3.4. is

  • is checks for object identity

  • is tests for identity, not equality

  • Compares id() output for both objects

  • CPython: compares the memory address a object resides in

  • Is used for checking if None

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

3.4.1. Test if empty

if name is None:
    print('Your name is empty')
else:
    print(f'Hello {name}')

3.4.2. Test if value is equal

Warning

In Python 3.8 the 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.

Listing 386. Bad
 if name is 'Mark Watney':
    print('You are Space Pirate!')
 else:
    print('You are just a regular astronaut...')
Listing 387. Good
 if name == 'Mark Watney':
    print('You are Space Pirate!')
 else:
    print('You are just a regular astronaut...')

3.4.3. Using is in script

  • both objects has the same id.

Listing 388. Using this code in script.
a = 'Jan Twardowski'
b = 'Jan Twardowski'

print(a)        # Jan Twardowski
print(b)        # Jan Twardowski

print(a == b)   # True
print(a is b)   # True

print(id(a))    # 4430933296
print(id(b))    # 4430933296

3.4.4. Using is in REPL (evaluated line by line)

Listing 389. Evaluated in REPL line by line.
a = 'Jan Twardowski'
b = 'Jan Twardowski'

print(a)        # Jan Twardowski
print(b)        # Jan Twardowski

print(a == b)   # True
print(a is b)   # False

print(id(a))    # 4784790960
print(id(b))    # 4784791408

3.4.5. Using is in REPL (evaluated at once)

Listing 390. Evaluated in REPL at once.
a = 'Jan Twardowski'
b = 'Jan Twardowski'

print(a)        # Jan Twardowski
print(b)        # Jan Twardowski

print(a == b)   # True
print(a is b)   # True

print(id(a))    # 4784833072
print(id(b))    # 4784833072