IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Méthode spéciale __mul__ [Python 3.X]


Sujet :

Python

  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    octobre 2017
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : octobre 2017
    Messages : 16
    Points : 15
    Points
    15
    Par défaut Méthode spéciale __mul__
    Bonjour,

    Je travaille en python depuis quelques mois et je suis actuellement sur un gros projet pour mon université. J'ai plutôt eu l'habitude de fortran ces dernières années donc j'ai encore beaucoup à apprendre en programmation orientée objet, et justement ma question touche à ça.

    Quitte à travailler en python, je me suis dit "autant profiter au maximum de la POO, même quand c'est pas nécessaire", et j'ai commencé à jouer avec les méthodes spéciales, comme __eq__. Mon problème concerne cependant plutôt __mul__. Le projet consiste à simuler un réseau cubique, et, sans entrer dans le détail technique, j'utilise une classe "point" à laquelle j'ai ajouté une méthode __mul__ pour le produit scalaire. Mon programme nécessite à un moment donné que je multiplie un point par un nombre, j'ai donc voulu ajouter ça et j'ai eu deux surprises. La première, c'est que l'interpréteur me renvoie une erreur de type
    l'opération * n'est pas implémentée pour 'float' et 'point'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class point :
        def __init__(self, x : int, y : int, z : int) :
            self.x = x
            self.y = y
            self.z = z
     
        def __mul__(self, other) :
            if isinstance(other, point):
                return self.x * other.x + self.y * other.y + self.z * other.z
            elif isinstance(other, float):
                return point(other * self.x, other * self.y, other * self.z)
            else :
                return NotImplemented
     
    truc = point(1,1,1)
    test = 5. * truc
    print (test)
    Ceci me renvoie donc l'erreur précédente. Ma deuxième surprise est que, sur base d'une hypothèse, j'ai commuté l'opération et ça a fonctionné :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    truc = point(1,1,1)
    test = truc * 5.
    print (test)
    Ceci fonctionne donc. Mon hypothèse est que, puisque de ce que j'ai cru comprendre en python tout est un objet, quand je fais 5. * truc, c'est la méthode __mul__ de float qui est appelée, alors que si je fais truc * 5., c'est la méthode __mul__ de point qui est appelée. Je vois alors deux solutions, mais je n'ai aucune idée de comment les appliquer :

    1. Modifier la méthode __mul__ de float
    2. Donner la priorité à la méthode __mul__ de point


    J'ai cherché un peu sur stackoverflow, mais je ne sais pas trop quels mots clefs utiliser pour trouver une réponse à ce genre de problème. Concrètement je peux contourner le problème en respectant le sens truc * 5., ou en implémentant une méthode non spéciale, mais je me dis que si c'est possible, en particulier la deuxième solution, ce serait quand même plus satisfaisant

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    juin 2008
    Messages
    19 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : juin 2008
    Messages : 19 593
    Points : 33 853
    Points
    33 853
    Par défaut
    Salut,

    p * 123 attrape le __mul__ de l'objet qui est à gauche.
    Pour faire marcher 123 * p, il faut définir __rmul__.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre éprouvé Avatar de LeNarvalo
    Homme Profil pro
    Amateur Python
    Inscrit en
    février 2014
    Messages
    545
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Amateur Python
    Secteur : Santé

    Informations forums :
    Inscription : février 2014
    Messages : 545
    Points : 907
    Points
    907
    Par défaut
    wiztricks

  4. #4
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 10 395
    Points : 28 267
    Points
    28 267
    Billets dans le blog
    1
    Par défaut
    Salut
    Citation Envoyé par theop8 Voir le message
    Modifier la méthode __mul__ de float
    Impossible, car un float peut être amené à multiplier n'importe quoi (et pas seulement un point).

    Citation Envoyé par theop8 Voir le message
    Quitte à travailler en python, je me suis dit "autant profiter au maximum de la POO, même quand c'est pas nécessaire"
    Excellent. Attention toutefois que ta méthode __mul__ gère les float mais pas les int (ne peut-on pas multiplier un point par un entier?) et que multiplier deux points devrait (à mon avis) donner un 3° point (et non la somme des valeurs multipliées).
    Il faut aussi penser à l'héritage. Si un utilisateur sous-classe ton point dans un objet à lui qu'il nommera "monPointMeilleur" et qu'il multiplie un objet à lui par un float, il aimerait peut-être recevoir en retour un "monPointMeilleur" et pas un simple point...
    Et il faut penser à celui qui voudra écrire point * (-point). Et au teubé qui lui tentera point * (+point) juste pour te faire ch...
    Hé oui, pas évident de faire de l'objet...

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    class point :
    	def __init__(self, x : int, y : int, z : int) -> object:
    		self.x = x
    		self.y = y
    		self.z = z
    	# __init__()
     
    	def __str__(self): return "x={}, y={}, z={}".format(self.x, self.y, self.z)
     
    	def __neg__(self): return self.__class__(-self.x, -self.y, -self.z)
    	def __pos__(self): return self.__class__(self.x, self.y, self.z)
     
    	def __mul__(self, other) :
    		if isinstance(other, point): pass
    		elif isinstance(other, (int, float)):
    			other=self.__class__(other, other, other)
    		else: raise TypeError(
    			"on ne peut pas multiplier un point avec un {}".format(type(other).__name__)
    		)
    		return self.__class__(other.x * self.x, other.y * self.y, other.z * self.z)
    	# __mul__()
     
    	def __rmul__(self, other): return self*other
    # point
     
    class superPoint(point): pass
     
    truc = superPoint(1,2,3)
    x=truc*5
    print(x, type(x))
    print(7.0*truc)
    print(truc*(+truc))
    print(-truc)
    print(truc*"hello")
    Ne te reste qu'à implémenter __add__, __radd__, __sub__, __rsub__, __truediv__, __rtruediv__, __floordiv__, __rfloordiv__, __pow__, __rpow__, __eq__, __ne__, __lt__, __le__, __gt__ et __ge__
    Et tu peux même rajouter éventuellement __imul__ (i=inplace) si tu veux que point*=n se comporte différemment de point=point*n (et leurs équivalents __iadd__, __isub__, __itruediv__, __ifloordiv__ et __ipow__ pour les opérations équivalentes)
    Et n'oublions pas __abs__, __bool__, __int__ et __float__ au cas où tu voudrais que abs(truc), bool(truc), int(truc) et float(truc) soient aussi possibles (comme __str__ permet de faire str(truc)).

    Bienvenue dans le monde merveilleux de la POO. Tu vas t'éclater

    Toutefois je te le redis, tu as tout à fait raison de penser objet. Parce que quand tu auras construit les bases, le reste ira super vite.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  5. #5
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    juillet 2006
    Messages
    3 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Alimentation

    Informations forums :
    Inscription : juillet 2006
    Messages : 3 225
    Points : 5 894
    Points
    5 894
    Par défaut
    Hello,

    Pour ce type d'exercice, j'aime bien utiliser les classes abstraites et séparer, c'est moins générique, mais ça permet d'éviter de tout péter si un changement a lieu dans la généricité de son algorithme. On ne modifie que l'algorithme de l'objet concerné.

    Un exemple rapide,

    EDIT: Modif avec les types

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    from __future__ import annotations
     
    from abc import ABC
    from abc import abstractmethod
     
     
    from typing import Union
     
     
     
     
    class Point(ABC):
        def __init__(self, *args: Union[float, int]):
     
     
            if len(args) > 3 or not args:
                raise TypeError("Not a structure Point")
     
     
            self.values = args
            self.res: tuple
     
     
        def __str__(self):
            return f"Point{repr(self.values)}"
     
     
        @abstractmethod
        def __mul__(self, point):
            """
            Détermine le produits scalaire
            """
     
     
            self.res = tuple((x*y for x, y in zip(self.values, point.values)))
     
     
    class Point2D(Point):
        def __init__(self, *args):
     
     
            if len(args) == 1:
                args = (args[0], args[0])
     
     
            if len(args) == 3:
                raise TypeError("Use Point3D class")
     
     
            super().__init__(*args)
     
     
        def __mul__(self, point: Point2D) -> Point2D:
            super().__mul__(point)
            return Point2D(*self.res)
     
     
     
     
    class Point3D(Point):
        def __init__(self, *args):
     
     
            if len(args) == 1:
                args = tuple((args[0] for _ in range(3)))
     
     
            if len(args) == 2:
                raise TypeError("Use Point2D class")
     
     
            super().__init__(*args)
     
     
        def __mul__(self, point: Point3D) -> Point3D:
            super().__mul__(point)
            return Point3D(*self.res)
     
     
    p_1 = Point2D(5)
    p_2 = Point2D(3, 5)
     
     
    print(p_1)
    print(p_2)
     
     
    print(p_1 * p_2)
     
     
    p_3 = Point3D(5)
    p_4 = Point3D(3, 2, 1)
    p_5 = Point3D(1, 1, 2)
     
     
    print(p_3)
    print(p_4)
    print(p_5)
     
     
    print(p_3 * p_4 * p_5)
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  6. #6
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 10 395
    Points : 28 267
    Points
    28 267
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Pour ce type d'exercice, j'aime bien utiliser les classes abstraites et séparer, c'est moins générique, mais ça permet d'éviter de tout péter si un changement a lieu dans la généricité de son algorithme. On ne modifie que l'algorithme de l'objet concerné.
    Attention toutefois parce que ta syntaxe ne supporte pas l'héritage

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class monPoint(Point2D): pass
     
    p_1 = monPoint(5)
    p_2 = monPoint(3, 5)
    print(p_1)
    print(p_2)
     
    x=p_1 * p_2
    print(x, type(x))		# => x de type Point2D alors qu'il devrait être de type monPoint

    Dans __mul__ faut remplacer return Point2D(*self.res) par return self.__class__(*self.res).

    Après rien n'empêche de faire pareil sans passer par abc. Cela évite de devoir réécrire __mul__ sans toutefois l'interdire si l'algo devait changer dans la sous-classe.
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  7. #7
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    juillet 2006
    Messages
    3 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Alimentation

    Informations forums :
    Inscription : juillet 2006
    Messages : 3 225
    Points : 5 894
    Points
    5 894
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Attention toutefois parce que ta syntaxe ne supporte pas l'héritage
    Parce-que je ne vois pas l'intérêt d'hériter justement de l'une de ces classes héritant de Point. Et je ne comprend pas pourquoi tu le fais car tu instancies monPoint qui fait la même chose que Point2D.

    Si tu m'expliques l'objectif d'hériter, je suis quasiment sûr de pouvoir l'adapter dans la classe Point2D, parce-que de mon côté je n'en vois pas...

    Citation Envoyé par Sve@r Voir le message
    Après rien n'empêche de faire pareil sans passer par abc. Cela évite de devoir réécrire __mul__ sans toutefois l'interdire si l'algo devait changer dans la sous-classe.
    Justement je veux retirer cette flexibilité, car à partir du moment où je crée un point (hérite de Point), je souhaite préciser le comportement de la multiplication entre deux points.

    Mais je suis d'accord sur le fait que cette manière pour des points précisément est un peu "overkill".
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  8. #8
    Membre émérite Avatar de papajoker
    Homme Profil pro
    Développeur Web
    Inscrit en
    septembre 2013
    Messages
    1 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nièvre (Bourgogne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : septembre 2013
    Messages : 1 382
    Points : 2 924
    Points
    2 924
    Par défaut
    bonjour

    Ce que je n'aime pas, c'est la création du self.res, avec cette technique(contournement), il va falloir créer un attribut par opération ...

    si on utilise ABC

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class Point(ABC):
     
        @staticmethod
        def _mul(point, point_second):
            """
            Détermine le produits scalaire
            """
            return tuple((x*y for x, y in zip(point.values, point_second.values)))
     
        @abstractmethod
        def __mul__(self, point):
            # pour descendants: return super().__mul__(point)
            return self.__class__(*self._mul(self, point))
     
     
        # autant retourner le bon type pour les descendants
        def __str__(self):
            return f"{self.__class__.__name__}{repr(self.values)}"
    $moi= ( !== ) ? : ;

  9. #9
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    juillet 2006
    Messages
    3 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Alimentation

    Informations forums :
    Inscription : juillet 2006
    Messages : 3 225
    Points : 5 894
    Points
    5 894
    Par défaut
    Citation Envoyé par papajoker Voir le message
    Ce que je n'aime pas, c'est la création du self.res, avec cette technique(contournement), il va falloir créer un attribut par opération ...
    Oui je comprends très bien, mais dans la tienne ce que je n'aime pas c'est que _mul et __mul__ font un peu doublon, mais j'avoue y avoir pensé avant d'écrire ma solution... c'est un choix discutable.

    EDIT: Pour moi j'appellerai pas cela un contournement, mais une étape de calcul avant la création de l'objet. L'avantage autre, c'est que je là crée une fois, je n'ai plus besoin de là rappeler et je peux réutiliser cette variable si besoin sans re-calcul.
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  10. #10
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 10 395
    Points : 28 267
    Points
    28 267
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Parce-que je ne vois pas l'intérêt d'hériter justement de l'une de ces classes héritant de Point.
    Ah tu ne peux pas présumer du besoin des utilisateurs. Si quelqu'un a envie de faire un Point2DPlus bien à lui, tu ne vas pas lui dire "mais pourquoi tu fais ça tu n'en as pas besoin". J'ai par exemple déjà créé des objets qui héritent d'objets officiels (exemple ConfigParser pour lequel j'ai rajouté des trucs dont j'avais besoin). L'héritage est un outil, tu ne peux pas empêcher les autres de l'utiliser si tel est leur envie.

    Citation Envoyé par fred1599 Voir le message
    Et je ne comprend pas pourquoi tu le fais car tu instancies monPoint qui fait la même chose que Point2D.
    Ah oui mais là c'était juste un exemple minimaliste pour montrer la faisabilité de la chose sur le forum. Evidemment que mon "monPoint" ici ne fait rien de plus que ton Point2D. Dans la vraie vie, celui qui voudra hériter de ton Point2D aura certainement de bonnes raisons de le faire. Par exemple il pourra vouloir instancier une méthode "toPolaire()" qui convertit les coordonnées cartésiennes (x, y) d'un point2D en coordonnées polaires (c'est juste un exemple, on peut en trouver d'autres)...

    Citation Envoyé par fred1599 Voir le message
    Justement je veux retirer cette flexibilité, car à partir du moment où je crée un point (hérite de Point), je souhaite préciser le comportement de la multiplication entre deux points.
    Mais je suis d'accord sur le fait que cette manière pour des points précisément est un peu "overkill".
    Ok je comprends mieux. C'est donc une volonté manifeste de faire ainsi, un choix assumé de tes objets. Ok si c'est un choix voulu en connaissance là évidemment tu es maitre de tes objets
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  11. #11
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    juillet 2006
    Messages
    3 225
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Alimentation

    Informations forums :
    Inscription : juillet 2006
    Messages : 3 225
    Points : 5 894
    Points
    5 894
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Ah tu ne peux pas présumer du besoin des utilisateurs. Si quelqu'un a envie de faire un Point2DPlus bien à lui, tu ne vas pas lui dire "mais pourquoi tu fais ça tu n'en as pas besoin". J'ai par exemple déjà créé des objets qui héritent d'objets officiels (exemple ConfigParser pour lequel j'ai rajouté des trucs dont j'avais besoin). L'héritage est un outil, tu ne peux pas empêcher les autres de l'utiliser si tel est leur envie.
    Oui je pense que ta dernière phrase indique que tu vois où je veux en venir, et en effet selon les besoins, l'une ou l'autre des méthodes sera plus adaptées, mais là mienne permet de rendre clair le type de retour sans utilisation d'isinstance je pense (préférer le polymorphisme aux if/else).
    Je m'évite des surprises selon souvent des changements courants... souvent au début on se fait avoir, et à juste titre, souvent on se dit, c'est redondant ! Plus tard, on se rend compte que c'est un sacré merdier. Je préfère modéliser plus, que de devoir adapter par la suite pour éviter de revenir en arrière, en codant de manière dégueulasse.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    from __future__ import annotations 
    from abc import ABC
    from abc import abstractmethod
     
     
    from typing import Union
     
     
     
     
    class Point(ABC):
        def __init__(self, *args: Union[float, int]):
     
     
            if len(args) > 3 or not args:
                raise TypeError("Not a structure Point")
     
     
            self.values = args
            self.res: tuple
     
     
        def __str__(self):
            return f"Point{repr(self.values)}"
     
     
        @abstractmethod
        def __mul__(self, point):
            """
            Détermine le produits scalaire
            """
     
     
            self.res = tuple((x*y for x, y in zip(self.values, point.values)))
     
     
    class Point2D(Point):
        def __init__(self, *args):
     
     
            if len(args) == 1:
                args = (args[0], args[0])
     
     
            if len(args) == 3:
                raise TypeError("Use Point3D class")
     
     
            super().__init__(*args)
     
     
        def __mul__(self, point: Point2D) -> Point2D:
            super().__mul__(point)
            return Point2D(*self.res)
     
     
     
     
    class Point3D(Point):
        def __init__(self, *args):
     
     
            if len(args) == 1:
                args = tuple((args[0] for _ in range(3)))
     
     
            if len(args) == 2:
                raise TypeError("Use Point2D class")
     
     
            super().__init__(*args)
     
     
        def __mul__(self, point: Point3D) -> Point3D:
            super().__mul__(point)
            return Point3D(*self.res)
     
     
    class Point2DPlus(Point2D):
        def __init__(self, *args):
            super().__init__(*args)
     
     
            self.toto = "tata"
     
        def __mul__(self, point: Union[Point2D, Point2DPlus]) -> Point2DPlus:
            super().__mul__(point)
            return Point2DPlus(*self.res)
     
     
     
    p_1 = Point2D(5)
    p_2 = Point2D(3, 5)
     
     
    print(p_1)
    print(p_2)
     
     
    print(p_1 * p_2)
     
     
    p_3 = Point3D(5)
    p_4 = Point3D(3, 2, 1)
    p_5 = Point3D(1, 1, 2)
     
     
    print(p_3)
    print(p_4)
    print(p_5)
     
     
    print(p_3 * p_4 * p_5)
     
     
    p_6 = Point2DPlus(5)
    p_7 = Point2DPlus(4, 3)
     
     
    p_8 = p_6 * p_7
    print(type(p_8))
    Citation Envoyé par Sve@r Voir le message
    Ah oui mais là c'était juste un exemple minimaliste pour montrer la faisabilité de la chose sur le forum. Evidemment que mon "monPoint" ici ne fait rien de plus que ton Point2D. Dans la vraie vie, celui qui voudra hériter de ton Point2D aura certainement de bonnes raisons de le faire. Par exemple il pourra vouloir instancier une méthode "toPolaire()" qui convertit les coordonnées cartésiennes (x, y) d'un point2D en coordonnées polaires (c'est juste un exemple, on peut en trouver d'autres)...
    Je sais bien, je taquine

    Je vois très bien où tu veux en venir, et comme je l'ai dis précédemment, rien est imposé, on reste sur l'imagination d'un besoin client que chacun ne connaît pas.

    Citation Envoyé par Sve@r Voir le message
    Ok je comprends mieux. C'est donc une volonté manifeste de faire ainsi, un choix assumé de tes objets. Ok si c'est un choix voulu en connaissance là évidemment tu es maitre de tes objets
    Si tu veux c'est une habitude de travail, surtout quand les besoins métier sont assez flou, je faisais partager mon expérience sur le sujet
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  12. #12
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 10 395
    Points : 28 267
    Points
    28 267
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par fred1599 Voir le message
    Je sais bien, je taquine
    Ah ouais ok. Bon ben la next time mets des emoticons que je sache que c'est du taquinage, taquin va...
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  13. #13
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    octobre 2017
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : octobre 2017
    Messages : 16
    Points : 15
    Points
    15
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Salut,

    p * 123 attrape le __mul__ de l'objet qui est à gauche.
    Pour faire marcher 123 * p, il faut définir __rmul__.

    - W
    Je répond un peu tard. Merci pour cette réponse simple et efficace

  14. #14
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    octobre 2017
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : octobre 2017
    Messages : 16
    Points : 15
    Points
    15
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Salut

    Impossible, car un float peut être amené à multiplier n'importe quoi (et pas seulement un point).
    Ok ok, c'était juste une hypothèse de ma part

    Citation Envoyé par Sve@r Voir le message

    Excellent. Attention toutefois que ta méthode __mul__ gère les float mais pas les int (ne peut-on pas multiplier un point par un entier?) et que multiplier deux points devrait (à mon avis) donner un 3° point (et non la somme des valeurs multipliées).
    Il faut aussi penser à l'héritage. Si un utilisateur sous-classe ton point dans un objet à lui qu'il nommera "monPointMeilleur" et qu'il multiplie un objet à lui par un float, il aimerait peut-être recevoir en retour un "monPointMeilleur" et pas un simple point...
    Et il faut penser à celui qui voudra écrire point * (-point). Et au teubé qui lui tentera point * (+point) juste pour te faire ch...
    Hé oui, pas évident de faire de l'objet...

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    class point :
    	def __init__(self, x : int, y : int, z : int) -> object:
    		self.x = x
    		self.y = y
    		self.z = z
    	# __init__()
     
    	def __str__(self): return "x={}, y={}, z={}".format(self.x, self.y, self.z)
     
    	def __neg__(self): return self.__class__(-self.x, -self.y, -self.z)
    	def __pos__(self): return self.__class__(self.x, self.y, self.z)
     
    	def __mul__(self, other) :
    		if isinstance(other, point): pass
    		elif isinstance(other, (int, float)):
    			other=self.__class__(other, other, other)
    		else: raise TypeError(
    			"on ne peut pas multiplier un point avec un {}".format(type(other).__name__)
    		)
    		return self.__class__(other.x * self.x, other.y * self.y, other.z * self.z)
    	# __mul__()
     
    	def __rmul__(self, other): return self*other
    # point
     
    class superPoint(point): pass
     
    truc = superPoint(1,2,3)
    x=truc*5
    print(x, type(x))
    print(7.0*truc)
    print(truc*(+truc))
    print(-truc)
    print(truc*"hello")
    Ne te reste qu'à implémenter __add__, __radd__, __sub__, __rsub__, __truediv__, __rtruediv__, __floordiv__, __rfloordiv__, __pow__, __rpow__, __eq__, __ne__, __lt__, __le__, __gt__ et __ge__
    Et tu peux même rajouter éventuellement __imul__ (i=inplace) si tu veux que point*=n se comporte différemment de point=point*n (et leurs équivalents __iadd__, __isub__, __itruediv__, __ifloordiv__ et __ipow__ pour les opérations équivalentes)
    Et n'oublions pas __abs__, __bool__, __int__ et __float__ au cas où tu voudrais que abs(truc), bool(truc), int(truc) et float(truc) soient aussi possibles (comme __str__ permet de faire str(truc)).

    Bienvenue dans le monde merveilleux de la POO. Tu vas t'éclater

    Toutefois je te le redis, tu as tout à fait raison de penser objet. Parce que quand tu auras construit les bases, le reste ira super vite.
    Merci pour ces détails supplémentaires.

    Concernant la possibilité de multiplier par un int je m'étais déjà posé la question en attendant la réponse et j'ai changé ma condition en if isinstance(other, float) or isinstance(other, int). J'ignore si isinstance() possède une écriture plus compacte pour ce genre de situation ou si il existe une autre fonction justement. C'est un détail sans réelle importance je pense.

    Pour __rmul__ je m'étais embêté à recopier les conditions de __mul__. Je n'aurais pas pensé spontanément à écrire return self*other, mais ça parait évident une fois qu'on l'a vu.

    Pour le reste, ça me donne des pistes pour l'avenir, mais dans le contexte de ce projet je ne pense pas aller aussi loin. Ce code est voué à une application très spécifique dans le cadre d'un projet bien particulier, et je doute qu'il soit un jour publié. La deadline pour la version 1.0 est dans 3 semaines, mais ce projet continuera l'an prochain donc j'aurais l'occasion de revenir dessus

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    juin 2008
    Messages
    19 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : juin 2008
    Messages : 19 593
    Points : 33 853
    Points
    33 853
    Par défaut
    Citation Envoyé par theop8 Voir le message
    J'ignore si isinstance() possède une écriture plus compacte pour ce genre de situation.
    isinstance(number, (int, float))
    Citation Envoyé par theop8 Voir le message
    Pour __rmul__ je m'étais embêté à recopier les conditions de __mul__. Je n'aurais pas pensé spontanément à écrire return self*other, mais ça parait évident une fois qu'on l'a vu.
    Moins évident est __rmul__ = __mul__ qui devrait suffire.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #16
    Membre émérite Avatar de papajoker
    Homme Profil pro
    Développeur Web
    Inscrit en
    septembre 2013
    Messages
    1 382
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nièvre (Bourgogne)

    Informations professionnelles :
    Activité : Développeur Web
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : septembre 2013
    Messages : 1 382
    Points : 2 924
    Points
    2 924
    Par défaut
    bonjour
    Citation Envoyé par wiztricks Voir le message
    Moins évident est __rmul__ = __mul__ qui devrait suffire.
    Ce qui me pose une question (idiote, car je pourrais tester...)

    Cela n'est pas généralement une mauvaise pratique en cas d'héritage ?

    Si le descendant redéfini uniquement __mul__(), le __rmul__() du descendant ne va pas pointer vers la méthode du parent et non la nouvelle méthode de sa classe ?

    EDIT: test fait, effectivement pas bon

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Papa():
        def un(self):
            print("papa", 1)
        deux = un
     
    class Enfant(Papa):
        def un(self):
            print("enfant", 1)
     
    p = Papa()
    p.un()
    p.deux()
     
    p = Enfant()
    p.un()
    p.deux()
    Enfant.deux() appelle bien la méthode du parent ; l'héritage est bien cassé
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    papa 1
    papa 1
    enfant 1
    papa 1
    $moi= ( !== ) ? : ;

  17. #17
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    février 2006
    Messages
    10 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : février 2006
    Messages : 10 395
    Points : 28 267
    Points
    28 267
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par theop8 Voir le message
    J'ignore si isinstance() possède une écriture plus compacte pour ce genre de situation ou si il existe une autre fonction justement.
    C'était écrit dans mon code...

    Citation Envoyé par papajoker Voir le message
    Ce qui me pose une question (idiote, car je pourrais tester...)
    Cela n'est pas généralement une mauvaise pratique en cas d'héritage ?
    Si le descendant redéfini uniquement __mul__(), le __rmul__() du descendant ne va pas pointer vers la méthode du parent et non la nouvelle méthode de sa classe ?
    Ben on n'a qu'à tester...

    Code python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    class point :
    	def __init__(self, x : int, y : int, z : int) -> object:
    		self.x = x
    		self.y = y
    		self.z = z
    	# __init__()
     
    	def __str__(self): return "x={}, y={}, z={}".format(self.x, self.y, self.z)
     
    	def __mul__(self, other) :
    		print("point.mul")
    		if isinstance(other, point): pass
    		elif isinstance(other, (int, float)):
    			other=self.__class__(other, other, other)
    		else: raise TypeError(
    			"on ne peut pas multiplier un point avec un {}".format(type(other).__name__)
    		)
    		return self.__class__(other.x * self.x, other.y * self.y, other.z * self.z)
    	# __mul__()
     
    	#def __rmul__(self, other): return self*other
    	__rmul__=__mul__
    # point
     
    class superPoint(point):
    	def __mul__(self, other) :
    		print("superPoint.mul")
    		return super().__mul__(other)
    	# __mul__()
    # superPoint()
     
    truc = superPoint(1,2,3)
    x=truc*5			# Multiplication témoin
    print(x, type(x))
    x=5*truc			# Multiplication renversée qui appelle __rmul__. On verra là lequel est appelé
    print(x, type(x))
    Résultat
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    superPoint.mul
    point.mul
    x=5, y=10, z=15 <class '__main__.superPoint'>
    point.mul
    x=5, y=10, z=15 <class '__main__.superPoint'>
    Donc le premier "superPoint.mul" affiché c'est l'appel de la multiplication témoin. Mais effectivement le 5*truc appelant le rmul aurait dû afficher aussi un second "superPoint.mul" en dessous.

    Maintenant si on décommente le rmul et qu'on commente le __rmul__=__mul__ de la ligne du dessous...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    superPoint.mul
    point.mul
    x=5, y=10, z=15 <class '__main__.superPoint'>
    superPoint.mul
    point.mul
    x=5, y=10, z=15 <class '__main__.superPoint'>
    Là on voit bien le second affichage se faire, montrant bien qu'on appelle le __mul__ hérité et non le __mul__ de l'ancètre.

    Ta remarque était bien vue
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site

  18. #18
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    juin 2008
    Messages
    19 593
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : juin 2008
    Messages : 19 593
    Points : 33 853
    Points
    33 853
    Par défaut
    Salut,

    Citation Envoyé par papajoker Voir le message
    Cela n'est pas généralement une mauvaise pratique en cas d'héritage ?
    Dans la pratique, si on utilise une classe Point qui définit nombre d'opérateurs magiques, il ne sera jamais simple de les surcharger sans lire une documentation (ou le code de la classe).... et d'avoir une batterie de tests qui assure la non-régression.

    Pire quand on fait de la POO, on va éviter l'héritage comme la peste (parfois à tord mais c'est comme ça).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  19. #19
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    octobre 2017
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : octobre 2017
    Messages : 16
    Points : 15
    Points
    15
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    C'était écrit dans mon code...
    Désolé... j'avais pas vu

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Python 3.X] Méthodes spéciales dans les classes
    Par badius1 dans le forum Général Python
    Réponses: 4
    Dernier message: 16/08/2021, 13h50
  2. Surcharger une méthode spéciale d'une class créée dynamiquement
    Par Battant dans le forum Général Python
    Réponses: 3
    Dernier message: 13/06/2017, 22h20
  3. Réponses: 8
    Dernier message: 27/01/2014, 10h36
  4. Probleme d'impression avec la méthode TForm->Print()
    Par Kid Icarus dans le forum C++Builder
    Réponses: 13
    Dernier message: 31/07/2002, 14h26
  5. Cryptage en C selon la méthode de césat
    Par shenron dans le forum C
    Réponses: 2
    Dernier message: 31/05/2002, 08h22

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo