Redéfinir un opérateur
Résolu
MemeTech
Messages postés
90
Date d'inscription
Statut
Membre
Dernière intervention
-
MemeTech Messages postés 90 Date d'inscription Statut Membre Dernière intervention -
MemeTech Messages postés 90 Date d'inscription Statut Membre Dernière intervention -
Bonjour !
Le titre n'est pas très explicite, je vais donc davantage développer :
J'ai une application de maths en lignes de commande où l'utilisateur peut entrer des formules sur l'interpréteur, dont les opérations de base.
Il a la possibilité de changer la précision des résultats, les convertir...
Il y a donc toute une série de traitements à effectuer sur les résultats et quand l'utilisateur fait une opération de base, mon programme ne peut pas les faire car ce n'est pas moi qui ai conçu les opérateurs pour les nombres int et float.
Comment faire pour quand même appliquer ces traitements aux résultats ?
Merci d'avance !
Le titre n'est pas très explicite, je vais donc davantage développer :
J'ai une application de maths en lignes de commande où l'utilisateur peut entrer des formules sur l'interpréteur, dont les opérations de base.
Il a la possibilité de changer la précision des résultats, les convertir...
Il y a donc toute une série de traitements à effectuer sur les résultats et quand l'utilisateur fait une opération de base, mon programme ne peut pas les faire car ce n'est pas moi qui ai conçu les opérateurs pour les nombres int et float.
Comment faire pour quand même appliquer ces traitements aux résultats ?
Merci d'avance !
6 réponses
Bonjour,
Toutes les opérations passent par les méthodes spéciales de python, par ex. quand on fait 1 + 2, cela passe par la méthode int.__add__
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
On ne peut heureusement pas redéfinir les méthodes des types de bases python, sinon bonjour les bugs que cela pourrait engendrer.
La seule solution est donc de définir ton propre type.
C'est plus contraignant que de saisir un simple nombre, mais au moins on a le choix.
Toutes les opérations passent par les méthodes spéciales de python, par ex. quand on fait 1 + 2, cela passe par la méthode int.__add__
https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types
On ne peut heureusement pas redéfinir les méthodes des types de bases python, sinon bonjour les bugs que cela pourrait engendrer.
>>> int.__add__ = lambda s, v: s + v Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can't set attributes of built-in/extension type 'int'
La seule solution est donc de définir ton propre type.
import math class Int: def __init__(self, v): self.v = v def __mul__(self, v): print('on me demande de multiplier') return round(self.v * v, 3) sqrt = lambda n: round(math.sqrt(n), 3) print(Int(5) * sqrt(2))
C'est plus contraignant que de saisir un simple nombre, mais au moins on a le choix.
yg_be
Messages postés
23541
Date d'inscription
Statut
Contributeur
Dernière intervention
Ambassadeur
1 584
bonjour, tu as donc une application, un utilisateur, un interpréteur, une série de traitements, des résultats, et un programme.
quel est le lien entre tout cela?
quel composant essaies-tu de créer ou de modifier?
quel est le lien entre tout cela?
quel composant essaies-tu de créer ou de modifier?
Désolé pour le retard.
En gros, mon appli charge des fonctions mathématiques et des classes dans l'interpréteur Python que l'utilisateur peut utiliser :
>>> prime (97) # L'utilisateur veut savoir si 97 est premier.
True
>>> _ # L'utilisateur est invité à taper une autre commande.
Si celui-ci tape :
>>> sqrt (2) # Pas besoin de davantage expliciter :-D
1.414
On voit ici que la précision à été limitée à trois décimales car j'ai écrit cette fonction, la valeur de retour est donc traitée.
Autre exemple :
>>> 5 * sqrt (2)
7.069999999999999
Hum !...
Vous avez très sûrement cerné le problème ; - )
Je souhaite donc pouvoir traiter aussi les résultats avec les opérations de base
En espérant avoir été plus clair cette fois !
En gros, mon appli charge des fonctions mathématiques et des classes dans l'interpréteur Python que l'utilisateur peut utiliser :
>>> prime (97) # L'utilisateur veut savoir si 97 est premier.
True
>>> _ # L'utilisateur est invité à taper une autre commande.
Si celui-ci tape :
>>> sqrt (2) # Pas besoin de davantage expliciter :-D
1.414
On voit ici que la précision à été limitée à trois décimales car j'ai écrit cette fonction, la valeur de retour est donc traitée.
Autre exemple :
>>> 5 * sqrt (2)
7.069999999999999
Hum !...
Vous avez très sûrement cerné le problème ; - )
Je souhaite donc pouvoir traiter aussi les résultats avec les opérations de base
En espérant avoir été plus clair cette fois !
Ce code est découpé en deux grandes parties : une pour les fonctions de base (sqrt, prime ou encore les fonctions trigonométriques), une pour tous les modules que l'utilisateur a le choix de charger au démarrage (Des classes pour manipuler des objets comme des vecteurs, des boules ou des rectangles).
Il y en a une dernière pour charger un certain nombre de constantes mathématiques comme pi, e ou encore la masse du Soleil, d'un proton...
Pour obtenir un retour formaté (pour éviter les résultats du genre -0.000000000000001 ou (3j - 0), quoi), j'ai fait une fonction qui traite le nombre et le renvoie bien propre et arrondi au nombre de décimales qu'on lui a demandé.
C'est la fonction trans (x) qu'on retrouve souvent.
Je ne vais pas mettre son code ici, ce serait inutile, en plus, il est moche : - )
Voilà un aperçu de mon code (pas trop non plus, il fait à peu près 4000 lignes :-D):
Au passage, désolé, je ne sais pas comment mettre un spoiler...
Partie constantes :
La partie fonctions :
Dernière partie sur les objets (Je mets uniquement le cube) :
Je simplifie aussi en retirant les opérateurs pour les homothéties.
De plus, la fonction isOK (x) sert simplement à savoir si x est un réel positif différent de zéro (pour éviter que les petits malins ne s'amusent à mettre une arête négative au cube : - |)
La fonction trans (x) à été simplifiée et spécialisée en formated (x)
Vous voyez donc pourquoi le traitement des résultats des opérations de base me pose problème.
Il y en a une dernière pour charger un certain nombre de constantes mathématiques comme pi, e ou encore la masse du Soleil, d'un proton...
Pour obtenir un retour formaté (pour éviter les résultats du genre -0.000000000000001 ou (3j - 0), quoi), j'ai fait une fonction qui traite le nombre et le renvoie bien propre et arrondi au nombre de décimales qu'on lui a demandé.
C'est la fonction trans (x) qu'on retrouve souvent.
Je ne vais pas mettre son code ici, ce serait inutile, en plus, il est moche : - )
Voilà un aperçu de mon code (pas trop non plus, il fait à peu près 4000 lignes :-D):
Au passage, désolé, je ne sais pas comment mettre un spoiler...
Partie constantes :
pi = 3.141592653589793238462 e = 2.71828182845904523536 phi = 1.618033988749894848204 G = 6.6743015e-11 i = 1j j = 1j c = 299792458 E = 1.602176634e-19 # Plus loin... # Je ne mets qu'une seule classe, il en a trois, mais elles fonctionnent de la même façon class MeasuresConstants: # In meters def __init__ (self): self._inch = 0.0254 # Le programme stocke les valeurs exactes et les renvoie formatées à la demande avec des property self._foot = 0.3048 self._yard = 0.9144 self._nauticalMile = 1852 self._league = 4828.032 self._ua = 149597870000 self._ly = 9460730472580800 self._parsec = 30856775670528308 def _getInch (self): return trans (self._inch) # Valeur de retour formatée def _getFoot (self): return trans (self._foot) def _getYard (self): return trans (self._yard) def _getNauticalMile (self): return trans (self._nauticalMile) def _getLeague (self): return trans (self._league) def _getUa (self): return trans (self._ua) def _getLy (self): return trans (self._ly) def _getParsec (self): return trans (self._parsec) def _setSomething (self, nValue): # On ne peut rien changer ici, on se fait donc gronder si on tente de modifier quoi que ce soit ! print ("\n You aren't allowed to change a constant value !\n") inch = property (_getInch, _setSomething) foot = property (_getFoot, _setSomething) yard = property (_getYard, _setSomething) nauticalMile = property (_getNauticalMile, _setSomething) league = property (_getLeague, _setSomething) ua = property (_getUa, _setSomething) ly = property (_getLy, _setSomething) parsec = property (_getParsec, _setSomething) m = MeasuresConstants (); # L'utilisateur y accède par m.nom_de_la_constante
La partie fonctions :
def frequence (duration, t = 1): # Renvoie une fréquence à partir d'une période et du nombre de répétitions dans cette période return trans (1 / (duration / t)) def sRadius (x): # Renvoie le rayon de Schwarzschild d'un trou noir en mètres return trans ((2 * 6.67408e-11 * x) / 299792458**2) def average (*params): # Revoie la moyenne de ses arguments values = list (params) summ = 0 i = len (values) - 1 while i > -1: summ += values[i] i -= 1 return trans (summ / len (values))
Dernière partie sur les objets (Je mets uniquement le cube) :
Je simplifie aussi en retirant les opérateurs pour les homothéties.
De plus, la fonction isOK (x) sert simplement à savoir si x est un réel positif différent de zéro (pour éviter que les petits malins ne s'amusent à mettre une arête négative au cube : - |)
La fonction trans (x) à été simplifiée et spécialisée en formated (x)
class Cube: def __init__ (self, edge = 1): # Quand on créé le cube if isOK (edge): self._edge = edge else: print ("\n Invalid edge : it must be a positive non-zero real number !\n Initialization of default values...\n Done\n") self._edge = 1 def _getEdge (self): return formated (self._edge) # Retour formaté def _getVolume (self): return formated (self._edge**3) def _getSurface (self): return formated (self._edge**2 * 6) def _getDiagonale (self): return formated (self._edge * 1.732050807568878) def _setEdge (self, nEdge): # Quand on veut changer l'arête du cube if isOK (nEdge): self._edge = nEdge else: print ("\n Invalid new edge : it must be a positive non-zero real number !\n") def _setVolume (self, nVolume): if isOK (nVolume): self._edge = nVolume**(1 / 3) else: print ("\n Invalid new volume : it must be a positive non-zero real number !\n") def _setSurface (self, nSurface): if isOK (nSurface): self._edge = (nSurface / 6)**0.5 else: print ("\n Invalid new surface : it must be a positive non-zero real number !\n") def _setDiagonale (self, nDiadonale): if isOK (nDiagonale): self._edge = nDiadonale / 1.732050807568878 else: print ("\n Invalid new diagonale : it must be a positive non-zero real number !\n") def __getattr__ (self, name): # Si l'utilisateur essaie d'accéder à un attribut qui n'existe pas print ("\n I'm so sorry, but there's no attribute named \"{}\" in this object !\n".format (name)) def __repr__ (self): # Lance une représentation graphique du cube print ("\n Loading resources...") system ('Graphics.pyw cube {0} {1} {2} {3}'.format (self.edge, self.volume, self.surface, self.diagonale)) return "\n Done !\n" edge = property (_getEdge, _setEdge) volume = property (_getVolume, _setVolume) surface = property (_getSurface, _setSurface) diagonale = property (_getDiagonale, _setDiagonale)
Vous voyez donc pourquoi le traitement des résultats des opérations de base me pose problème.
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question
Merci pour votre réponse !
En effet, votre méthode paraît tout à fait logique, mais comment faire pour éviter au pauvre utilisateur de se coltiner l'écriture :
>>> Int (5) + Float (6.9)
Merci !
En effet, votre méthode paraît tout à fait logique, mais comment faire pour éviter au pauvre utilisateur de se coltiner l'écriture :
>>> Int (5) + Float (6.9)
Merci !
Justement, j'aimerais faire cela MAIS :
Pour faire une interface, ça va.
Par contre, pour interpréter les commandes de l'utilisateur, c'est un foutoir incroyable :
il y a deux fonctions pour éxécuter une commande Python : exec (cmd) et eval (cmd).
Il faut faire un mélange fumeux des deux car eval () fonctionnera bien pour les opérations,
exec () pour la création des objets...
Bref, entre les exeptions et les bugs incompréhensibles, je ne m'y retrouve pas.
Peut-être existe-il une fonction plus universelle pour cela ?
Pour faire une interface, ça va.
Par contre, pour interpréter les commandes de l'utilisateur, c'est un foutoir incroyable :
il y a deux fonctions pour éxécuter une commande Python : exec (cmd) et eval (cmd).
Il faut faire un mélange fumeux des deux car eval () fonctionnera bien pour les opérations,
exec () pour la création des objets...
Bref, entre les exeptions et les bugs incompréhensibles, je ne m'y retrouve pas.
Peut-être existe-il une fonction plus universelle pour cela ?
Bonjour,
Je ne connais pas nyanmaths (c'est un module python ? une app externe ?), mais ça ne change pas grand chose à l'histoire, soit tu laisses l'utilisateur en roue libre, soit tu implémentes tout un tas de fonctionnalités via une interface graphique et là c'est un sacré boulot qui va demander beaucoup de temps.
Je ne connais pas nyanmaths (c'est un module python ? une app externe ?), mais ça ne change pas grand chose à l'histoire, soit tu laisses l'utilisateur en roue libre, soit tu implémentes tout un tas de fonctionnalités via une interface graphique et là c'est un sacré boulot qui va demander beaucoup de temps.
En effet, pas bête, peut-être en contrôlant ce qui est donné en entrée ou fait en sortie avec sys.stdin et sys.stdout, mais je ne sais pas si c'est possible de le faire dans l'interpréteur python, il faudrait tester ^^
Il me semblait que j'avais bien évité ce genre de choses, pourtant...
par exemple sqrt(sqrt(2)), pourquoi arrondir deux fois?
quand tu fais sqrt(2)*1000, à quel moment veux-tu arrondir?