15. Object Identity

15.1. Hash

  • Funkcja hash zwraca int

  • set() można zrobić z dowolnego hashowalnego obiektu

  • dict() może mieć klucze, które są dowolnym hashowalnym obiektem

  • 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 309. dict() może mieć klucze, które są dowolnym hashowalnym obiektem
key = 'last_name'

my_dict = {
    'fist_name': 'key can be ``str``',
    key: 'key can be ``str``',
    1: 'key can be ``int``',
    1.5: 'key can be ``float``',
    (1, 2): 'key can be ``tuple``',
}
Listing 310. set() można zrobić z dowolnego hashowalnego obiektu
class Astronaut:
    def __init__(self, name):
        self.name = name


{1, 1, 2}
# {1, 2}

jose = Astronaut(name='Jose Jimenez')
data = {jose, jose}
len(data)
# 1

data = {Astronaut(name='Jose Jimenez'), Astronaut(name='Jose Jimenez')}
len(data)
# 2
Listing 311. Generating hash and object comparision
class Astronaut:
    def __init__(self, first_name, last_name, agency='NASA'):
        self.first_name = first_name
        self.last_name = last_name
        self.agency = agency

    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) + hash(self.agency)

    def __eq__(self, other):
        if self.first_name == other.first_name and \
                self.last_name == other.last_name and \
                self.agency == other.agency:
            return True
        else:
            return False
Listing 312. Generating hash and object comparision
from collections import OrderedDict


class Astronaut:
    def __init__(self, first_name, last_name, agency='NASA'):
        self.first_name = first_name
        self.last_name = last_name
        self.agency = agency

    def __hash__(self):
        d = OrderedDict(self.__dict__)
        return hash(d)

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

Note

Since Python 3.7 dict has fixed order and using OrderedDict is not necessary

15.2. is

  • is porównuje czy dwa obiekty są tożsame

  • Sprawdzenie odbywa się przez porównanie wartości id() dla obiektu

  • Najczęściej służy do sprawdzania czy coś jest None

15.2.1. Good

if name is None:
    print('Name is not set')
else:
    print('You have set your name')

15.2.2. Not good

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.

if name is 'Mark Watney':
   print('You are Space Pirate!')
else:
   print('You are not pirate at all!')

15.2.3. Using is in script

  • id() will change every time you execute script

  • both objects has the same id.

Listing 313. 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

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

Listing 314. 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

15.2.5. Using is in REPL (evaluated at once)

Listing 315. 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