Partage
  • Partager sur Facebook
  • Partager sur Twitter

Est ce que __iadd__ est une méthode spéciale?

Comment python sait quand activer une méthode spéciale

Sujet résolu
    3 août 2020 à 12:25:18

    Bonjour,

    Je n'ai pas très bien compris comment python comprends quand il faut qu'il utilise une méthode spéciale. Est-ce-que __iadd__ est une méthode spéciale? Pourtant J'ai essayé de changer de change __iadd__ en __azadd__ par exemple et le code fonctionne toujours. Pourquoi? Est-ce-que python a compris qu'il faut activer cette méthode ou c'est autre chose?

    Lien de la partie du cour : https://openclassrooms.com/fr/courses/235344-apprenez-a-programmer-en-python/233046-appliquez-des-methodes-speciales#/id/r-2233238

    Voici mon code :

    class Duree:
        """Classe contenant des durées sous la forme d'un nombre de minutes
        et de secondes"""
        
        def __init__(self, min=0, sec=0):
            """Constructeur de la classe"""
            self.min = min # Nombre de minutes
            self.sec = sec # Nombre de secondes
        def __str__(self):
            """Affichage un peu plus joli de nos objets"""
            return "{0:02}:{1:02}".format(self.min, self.sec)
        def __add__(self, objet_a_ajouter):
            """L'objet à ajouter est un entier, le nombre de secondes"""
            nouvelle_duree = Duree()
            # On va copier self dans l'objet créé pour avoir la même durée
            nouvelle_duree.min = self.min
            nouvelle_duree.sec = self.sec
            # On ajoute la durée
            nouvelle_duree.sec += objet_a_ajouter
            # Si le nombre de secondes >= 60
            if nouvelle_duree.sec >= 60:
                nouvelle_duree.min += nouvelle_duree.sec // 60
                nouvelle_duree.sec = nouvelle_duree.sec % 60
            # On renvoie la nouvelle durée
            return nouvelle_duree
        def __radd__(self, objet_a_ajouter):
            """Cette méthode est appelée si on écrit 4 + objet et que
            le premier objet (4 dans cet exemple) ne sait pas comment ajouter
            le second. On se contente de rediriger sur __add__ puisque,
            ici, cela revient au même : l'opération doit avoir le même résultat,
            posée dans un sens ou dans l'autre"""
            
            return self + objet_a_ajouter
        def __iadd__(self, objet_a_ajouter):
            """L'objet à ajouter est un entier, le nombre de secondes"""
            # On travaille directement sur self cette fois
            # On ajoute la durée
            self.sec += objet_a_ajouter
            # Si le nombre de secondes >= 60
            if self.sec >= 60:
                self.min += self.sec // 60
                self.sec = self.sec % 60
            # On renvoie self
            return self
    
    heure = Duree(37, 49)
    print(heure)
    # test de add
    heure = heure + 60
    print(heure)
    # test de add mais sur une autre variable
    d2 = heure.__add__(300)
    print(d2)
    # test de radd
    heure = 60 + heure
    print(heure)
    # test de iadd
    heure += 60
    print(heure)

    Merci d'avance !

    • Partager sur Facebook
    • Partager sur Twitter
      3 août 2020 à 14:44:42

      C'est bien une méthode spéciale, mais si tu ne la définis pas toi même l'opération "+=" appelle __add__ automatiquement à la place.

      -
      Edité par thelinekioubeur 3 août 2020 à 14:45:02

      • Partager sur Facebook
      • Partager sur Twitter
        3 août 2020 à 16:51:22

        Donc quelle est l'utilité de la définir? Est-ce pareil pour les méthodes de comparaison ainsi que toutes les autres?

        • Partager sur Facebook
        • Partager sur Twitter
          3 août 2020 à 19:12:08

          C'est utile de la définir seulement si tu veux un comportement différent.

          Pour les autres opérateur ça doit être pareil, mais pour les comparaisons je ne vois pas de quoi tu parles.

          • Partager sur Facebook
          • Partager sur Twitter
            4 août 2020 à 8:09:51

            Echo293 a écrit: > Donc quelle est l'utilité de la définir? Est-ce pareil pour les méthodes de comparaison ainsi que toutes les autres?

            On peut prendre l'exemple des listes de Python : la méthode __iadd__ y est utilisée pour modifier la liste en-place lors d'un +=, comme on le voit dans la méthode suivante :

            >>> a = b = []
            >>> a += [0, 1, 2]
            >>> a
            [0, 1, 2]
            >>> b
            [0, 1, 2]
            

            Cela ne serait pas possible avec juste la méthode __add__ (à moins de casser le fonctionnement normal de l'opération +), voilà le résultat qu'on aurait sans __iadd__ :

            >>> a = b = []
            >>> a = a + [0, 1, 2]
            >>> a
            [0, 1, 2]
            >>> b
            []
            

            Comme dit par thelinekioubeur cela permet donc d'avoir des différences de comportement entre les deux opérations (différences minimes généralement car il s'agit quand même d'une opération similaire).

            • Partager sur Facebook
            • Partager sur Twitter
              4 août 2020 à 11:57:23

              D'accord, merci à vous deux ! Donc on peut définir cette méthode spéciale pour qu'elle ait un comportement différent, et c'est pareil pour les autres méthodes, c'est bien cela? Merci!
              • Partager sur Facebook
              • Partager sur Twitter
                4 août 2020 à 12:55:53

                C'est pareil pour les autres méthodes __ixxx__ oui (__isub__, __imul__, etc.) pour tous les opérateurs ¤=.

                • Partager sur Facebook
                • Partager sur Twitter
                  4 août 2020 à 13:05:26

                  D'accord, merci beaucoup!

                  • Partager sur Facebook
                  • Partager sur Twitter
                    4 août 2020 à 14:12:03

                    Ouais alors par contre pour les listes c'est une bêtise de python imo :-°
                    • Partager sur Facebook
                    • Partager sur Twitter
                      4 août 2020 à 14:20:51

                      Ça dépend de la façon de voir les choses. La modification de la liste courante permet de gagner en performances parce qu'il n'y a pas besoin d'allouer un nouvel objet.

                      Ce serait aussi possible avec update, mais ça n'a pas la même lisibilité qu'un opérateur +.

                      • Partager sur Facebook
                      • Partager sur Twitter
                        4 août 2020 à 15:24:01

                        C'est extend, pas update.

                        En fait __iadd__ et extend font strictement la même chose sur les listes, c'est ça la bêtise je pense.

                        Au final j'utilise toujours extend et jamais +=. Car quand je fais += je m'attend justement à ce que ça alloue un nouvel objet, comme sur les autres types de base (qui sont certes imutables, je ne vois pas d'autres cas que list où le + est défini sur un mutable).

                        Edit: ah ya aussi bytearray, et comme pour les listes __iadd__ et extend font la même chose

                        -
                        Edité par thelinekioubeur 4 août 2020 à 15:33:46

                        • Partager sur Facebook
                        • Partager sur Twitter
                          4 août 2020 à 15:49:42

                          extenden effet. Mais en fait c'est le comportement de base sur tous les types mutables, oui, le i dans le nom de la méthode spéciale est d'ailleurs là pour in-place.

                          Tu auras la même chose sur les sets avec &=, |=, ^= et -=. En dehors des builtins, tu auras aussi ce comportement sur les types mutables de la bibliothèque standard, collections.Counter par exemple. C'est le comportement normal et voulu.

                          • Partager sur Facebook
                          • Partager sur Twitter
                            4 août 2020 à 20:30:02

                            Ok je comprend mieux. Mais du coup extend sert à rien non ?
                            • Partager sur Facebook
                            • Partager sur Twitter
                              4 août 2020 à 23:17:45

                              Globalement la différence est que l'une est une méthode et l'autre un opérateur, tu passeras plus facilement une méthode en paramètre par exemple.

                              Le fait que += soit un opérateur d'assignation implique aussi quelques différences à l'usage : il faut que ce qui soit à gauche soit une cible d'assignation valide (pas un appel de fonction par exemple), et comme il s'agit d'une redéfinition de variable ça peut poser problème pour les variables non locales.

                              • Partager sur Facebook
                              • Partager sur Twitter
                                5 août 2020 à 7:58:33

                                Oui après on peut très bien passer __iadd__ en paramètre.

                                Ça me semble quand même étrange comme choix. En principe on les méthodes qui modifient l'objet sur place, comme list.extend, ne renvoient rien, ou en tout cas pas self. Et list.__iadd__ fait exception car étant une méthode d'opérateur, elle doit forcément renvoyer qqchose...

                                Aussi on pourrait ptet trouver des usecase où on veut forcément un nouvel objet, où l'on devra faire "a = a + b", et là ça fait bizarre de ne pas utiliser +=

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  5 août 2020 à 9:23:55

                                  Ça a ses avantages et ses inconvénients.

                                  Mais passer x.__iadd__ en paramètre non, personne ne fait ça. Les méthodes spéciales n'ont normalement jamais besoin d'être appelées directement.

                                  Quant à vouloir travailler sur un nouvel objet, il y a aussi la solution de faire une copie au préalable. Mais oui, cela peut amener à des comportements perturbants.

                                  thelinekioubeur a écrit: > En principe on les méthodes qui modifient l'objet sur place, comme list.extend, ne renvoient rien

                                  C'est vrai pour les méthodes standard oui, mais les opérateurs sont un peu différents.

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    5 août 2020 à 9:36:42

                                    entwanne a écrit:

                                    Mais passer x.__iadd__ en paramètre non, personne ne fait ça. Les méthodes spéciales n'ont normalement jamais besoin d'être appelées directement.


                                    On voit parfois des trucs genre  "map(a.__getitem__, foo)". Et pour les opérateurs je vois aussi des "functools.partial(operator.iadd, x)" :lol:
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      5 août 2020 à 9:43:17

                                      thelinekioubeur a écrit: > On voit parfois des trucs genre "map(a.getitem, foo)".

                                      Je n'ai jamais vu, mais je trouve ça beaucoup moins élégant que operator.itemgetter par exemple (operator.itemgetter(foo)(a)).

                                      Mais oui, les appels à operator sont plus courant, par contre il faut préciser les opérandes (comme avec le partial ici). Je ne crois pas avoir déjà vu le cas que tu cites.

                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        5 août 2020 à 10:51:17

                                        En vrai jl'ai vu avec add, pas avec iadd
                                        • Partager sur Facebook
                                        • Partager sur Twitter

                                        Est ce que __iadd__ est une méthode spéciale?

                                        × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                        × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                        • Editeur
                                        • Markdown