Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

aussi appelées dunder methods

méthodes spéciales / dunder methods

c’est-à-dire donner un sens à des phrases commme:

len(obj)

class Classe:
    
    def __init__(self, students):
        self.students = students
        
    def __len__(self):
        return len(self.students)
The history saving thread hit an unexpected error (OperationalError('attempt to write a readonly database')).History will not be written to the database.
classe = Classe(['jean', 'laurent', 'benoit'])

len(classe)
3

de manière similaire :

opérateurs: obj1 + obj2

class Classe:
    
    def __init__(self, students):
        self.students = students
        
    def __add__(self, other):
        return Classe(self.students + other.students)
    
    def __repr__(self):
        return f"[{len(self.students)} students]"
classe1 = Classe(['marie', 'claire'])
classe2 = Classe(['jean', 'laurent'])

classe1 + classe2
[4 students]

itérations: for item in obj:

class Classe:

    def __init__(self, students):
        self.students = students

    def __iter__(self):
        """
        iterate on self as if it was self.students
        """
        return iter(self.students)
classe = Classe(['jean', 'laurent', 'benoit'])

for s in classe:
    print(s)
jean
laurent
benoit
# et même d'ailleurs
x, y, z = classe
y
'laurent'

appartenance: x in obj

on l’a vu déjà avec la classe Circle:

class Classe:

    def __init__(self, students):
        self.students = students

    def __contains__(self, student):
        return student in self.students
classe = Classe(['jean', 'laurent', 'benoit'])

'jean' in classe
True

indexations: obj[x]

class Classe:

    def __init__(self, students):
        self.students = students

    def __getitem__(self, index):
        if isinstance(index, int):
            return self.students[index]
        elif isinstance(index, str):
            if index in self.students:
                return index
            else:
                return None
classe = Classe(['jean', 'laurent', 'benoit'])

classe[1]
'laurent'
classe['jean']
'jean'
classe['pierre'] is None
True

classe callable: obj(x)

on peut même donner du sens à obj(x)

# make it callable

class Line:
    """
    the line of equation y = ax + b
    """
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
    def __call__(self, x):
        """
        can be used as function that
        computes y = ax+b given x
        """
        return self.a * x + self.b
# cet objet se comporte comme une fonction

line = Line(2, 2)


# du coup c'est intéressant de pouvoir l'appeler
# comme si c'était réellement une fonction

line(1)
4

classe sortable: obj < obj2

pour ne pas changer d’exemple, imaginons que l’on veuille pouvoir trier une collection d’instances de Line
pour cela il suffit d’expliciter l’ordre entre les instances
et pour cela une technique consiste à redéfinir la dunder __lt__ (pour lower than)

ça se présenterait comme suit

# make it sortable

class Line:

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def __repr__(self):
        return f"{self.a}x + {self.b}"


    # on définit l'ordre entre éléments, ici: self < other
    
    def __lt__(self, other):
        return (self.a, self.b) < (other.a, other.b)


lines = [ Line(3, 1), Line(1, 2), Line(3, -1), Line(2, 0)]
# du coup la classe sait comparer deux éléments entre eux

# prenons par exemple les deux premiers éléments
L1, L2, *_ = lines

# et comparons-les
L1 < L2
False
# du coup on peut trier ces éléments sans avoir à préciser la fonction de tri

sorted(lines)
[1x + 2, 2x + 0, 3x + -1, 3x + 1]

classe hashable: D[obj]

on a pu dire que les clés des dictionnaires devaient être des objets non mutables; c’est vrai pour les types de base du langage
par contre lorsqu’il s’agit de classes user-defined, cette contrainte est levée si la classe implémente le protocole dit des objets hashables

toujours avec notre exemple de la classe Line: on pourrait avoir envie de créer des ensembles d’objets de type Line; ou bien encore d’utiliser un objet Line comme un clé de dictionnaire

cela est possible par exemple comme ceci: la classe doit implémenter deux dunder méthodes __eq__ et __hash__

# make it hashable

class Line:
    
    def __init__(self, a, b):
        self.a = a
        self.b = b


    # pour définir à quelle condition deux objets
    # sont considérés égaux - au sens de == 
    
    def __eq__(self, other):
        return (self.a == other.a) and (self.f == other.b)


    # comment doit-on calculer le hash d'un objet Line ?

    def __hash__(self):
        # on n'a qu'à le hasher comme le tuple (a, b)
        return hash( (self.a, self.b) )
# on peut alors utiliser les objets dans un dict ou dans un set !

D, S = {}, set()
line = Line(0, 0)

D[line] = "Yes !"
S = {line, line}

print(f"{D=}, {len(S)=}")
D={<__main__.Line object at 0x7fb1adb67c80>: 'Yes !'}, len(S)=1

résumé

une classe peut définir des méthodes spéciales

pour en savoir plus

la (longue) liste exhaustive des méthodes spéciales est donnée dans la documentation officielle ici

https://docs.python.org/3/reference/datamodel.html#special-method-names