'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 ...
Il y a une bizarrerie...
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**********************
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.
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.
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()
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?
Partager