'Lazzy' est un mot à la mode. La 'lazzy evaluation' c'est 'fashion'.
Python n'est jamais en retard, en voici la preuve.
Dans un passé proche on ne disposait pas du type 'rationnel' en standard (il paraît que c'est fait depuis la version 2.6, mais j'attends encore un peu parce que la plupart des biblios sont soit incompatibles soit non à jour quoiqu'on dise...).
Quoiqu'il en soit j'ai relevé mes manches pour 'palucher' une classe des rationnels.
Ca donne à peu près ça. Je l'ai fait à l'instinct et par la suite, quand j'ai vu des propositions tierces, elles ressemblaient comme deux gouttes d'eau à la mienne. Comme quoi, dans ce cas d'école il n'y a pas 36 façons de faire. Bref ça donne ça ...
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
# -*- coding: utf-8 -*-
from math import *
 
# un module de modélisation des nombres rationnels
 
# pgcd de deux entiers positifs
#suivant l'algorithme d'Euclide
def pgcd(x,y):
    if(x==0 or y==0):
        return 1
    if (y%x==0):
        return x
    else:
        return pgcd(y%x,x)
    return
 
#*****************classe Rationnel*********************************
class Rationnel:
 
    #simplification des fractions
    def normalise(self):
        g = pgcd(abs(self.num), abs(self.den))
        self.num = self.num / g
        self.den = self.den / g
        if(self.num*self.den<0):
            if self.den<0:
                self.den=-self.den
                self.num=-self.num
        else:
            if self.den <0:
                self.den=-self.den
                self.num=-self.num
        return
 
    def __init__(self, num, den = 1):  # Constructeur
        if den == 0:
            raise ZeroDivisionError, 'denominateur nul'
        else:
            self.num=num
            self.den=den
            self.normalise()
        return
    #teste si la fraction est entière
    def entier(self):
        return self.num%self.den==0
    #teste si nulle
    def nul (self):
        return self.num==0
 
    """Les opérations binaires"""
    def __add__(self, other): #addition
        n=self.num*other.den+other.num*self.den
        d=self.den*other.den
        return Rationnel(n,d)
    def __rmul__(self,other): #multiplication
        n=self.num*other.num
        d=self.den*other.den
        return Rationnel(n,d)
    def __sub__(self, other): #soustraction
        n=self.num*other.den-other.num*self.den
        d=self.den*other.den
        return Rationnel(n,d)
    def __div__(self,other): #division
        if other.nul():
            raise ZeroDivisionError, 'diviseur nul'
        else:
            n=self.num*other.den
            d=self.den*other.num
            return Rationnel(n,d)
        return
    """opérateurs unaires"""
    def __neg__(self): #opposé
        return Rationnel(-self.num,self.den)
    def __invert__(self):
        if self.nul(): #inverse
            raise ZeroDivisionError, 'diviseur nul'
        else:
            return Rationnel (self.den,self.num)
        return
    def __pow__(self,n):
        if n==0:
            return Rationnel(1,1)
        elif n>0:
            return Rationnel (self.num**n,self.den**n)
        else:
            if self.nul():
                raise ZeroDivisionError, 'puissance negative d\' nul'
            else:
                return Rationnel(self.den**(-n),self.num**(-n))
        return
 
    """Les comparaisons"""
    def __eq__(self,other):
       return self.num*other.den==self.den*other.num
    def __lt__(self,other):
        return self.num*other.den<self.den*other.num
    def __le__(self,other):
        return self.num*other.den<=self.den*other.num
    def __gt__(self,other):
        return self.num*other.den>self.den*other.num
    def __ge__(self,other):
        return self.num*other.den>=self.den*other.num
    def __ne__(self,other):
       return self.num*other.den!=self.den*other.num
 
    # pour voir une fraction sur la console appelée par print
    def __str__(self):
        if self.entier():
            return str(self.num)
        else:
            return str(self.num)+'/'+str(self.den)
#************************fin de la définition**********************
Il y a une bizarrerie...
L'opérateur de multiplication est défini par une surcharge de __rmul__ et non de __mul__ (en gros pour ceux qui l'ignorent rmul c'est quand le self est l'opérande de droite.
Si vous surchargez __mul__ au lieu de __rmul__ cela marche aussi bien.
Venons en au fait. Mon but est de développer un module d'algèbre linéaire sur le corps Q (c'est d'ailleurs chose faite maintenant).
Alors à partir du type rationnel il faut développer les vecteurs à composantes rationnelles, les matrices à coeffs rat. etc, etc..
Commençons par les vecteurs.
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# -*- coding: utf-8 -*-
 
from rationnels import *
class VecteurQn:
    """modélisation des vecteurs à n dimensions rationnelles"""
    def __init__(self,L):
        """constructeur"""
        self.V=L
        self.n=len(L)
    def __str__(self):
        """resprésentation externe print, etc..."""
        L=[str(x) for x in self.V]
        return '('+ ",".join(L)+')'
    def nul(self):
        """test de nullité"""
        return all([x.nul() for x in self.V])
    def __add__(self,other):
        """addition des vecteurs"""
        if self.n!=other.n:
            raise 'size: ' 'Tailles non concordantes'
        else:
            L=[self.V[i]+other.V[i] for i in range(0,self.n)]
            return VecteurQn(L)
    def __neg__(self):
        """opposé d'un vecteur"""
        return VecteurQn([-x for x in self.V])
    def __sub__(self,other):
        """différence de deux vecteurs"""
        return self+(-other)
    def __rmul__(self,k):
        """ produit par un scalaire rationnel"""
        L=[k*x for x in self.V]
        return VecteurQn(L)
 
def main():
    """test de la classe ci-dessus"""
    half=Rationnel(1,2)
    third=Rationnel(1,3)
    one=Rationnel(1)
    L=[one,half, third]
    V1=VecteurQn(L)
    V2=V1
    print V1
    print V2
    print V1+V2
    print -V1
    V3=V1-V2
    print V3
    print V3.nul()
    V4=half*V1
    print V4
    print V4.nul()
    return
 
if __name__=='__main__':
    main()
Il faut définir le produit d'un vecteur par un scalaire et là ON N'A PAS LE CHOIX ! c'est bien __rmul__ qu'il faut surcharger.
Que se passe-t-il si on a surchargé __mul__ dans la classe Rationnel ?
Si je veux calculer (1/2)V il faudra écrire quelque chose comme :
Un_demi (bien frais) = Rationnel(1,2)
W=Un_demi*V
Et là notre Python paresseux va refuser avec un message abscons.
La raison !...
Il veut évaluer un produit a * b où a est rationnel alors il va chercher la surcharge de __mul__ dans la classe correspondante et il trouve que comme le second facteur n'est pas du type rationnel il ne peut pas faire le travail, et puis il va se coucher.
S'il n'était pas paresseux avant d'abandonner si rapidement il irait jeter un coup d'oeil sur l'opérande de droite et voir que pour sa classe il y a une surcharge de __rmul__.
Python a encore beaucoup à apprendre de C++, cela ne pourrait arriver en C++ où toutes les possibilités sont testées et les ambiguïtés mise ne relief quand il y en a.
Est-ce que j'ai bien compris?