Redéfinir un opérateur [Résolu]

Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
- - Dernière réponse : MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
- 3 nov. 2019 à 15:33
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 !
Afficher la suite 

6 réponses

Meilleure réponse
1
Merci
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.

>>> 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.

Dire « Merci » 1

Heureux de vous avoir aidé ! Vous nous appréciez ? Donnez votre avis sur nous ! Evaluez CommentCaMarche

CCM 70225 internautes nous ont dit merci ce mois-ci

yg_be
Messages postés
8966
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
22 novembre 2019
461 -
est-ce adéquat d'appliquer l'arrondi à chaque opération, n'est ce pas préférable d'arrondir uniquement avant l'affichage final?
> yg_be
Messages postés
8966
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
22 novembre 2019
-
C'était pour moi je pense.

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 ^^
MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
Dans le code de Khrug ou le mien ?
Il me semblait que j'avais bien évité ce genre de choses, pourtant...
yg_be
Messages postés
8966
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
22 novembre 2019
461 > MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
je vois que tu fais trans() dans beaucoup de fonctions, j'imagine qu'elles peuvent être combinées, et donc que tu fais appel à trans() sur des résultats intermédiaires.
par exemple sqrt(sqrt(2)), pourquoi arrondir deux fois?
quand tu fais sqrt(2)*1000, à quel moment veux-tu arrondir?
MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
Justement : trouver une fonction universelle pour éxécuter une commande permetterait de ne pas faire trop d'appels à trans (), car si cette fonction renvoie quelque chose, il n'y aura qu'un seul traitement à faire !
Commenter la réponse de khrug
Messages postés
8966
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
22 novembre 2019
461
0
Merci
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?
Commenter la réponse de yg_be
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
0
Merci
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 !
yg_be
Messages postés
8966
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
22 novembre 2019
461 -
peux-tu partager le code de ton appli, et donner un exemple de formule que tu veux également pouvoir traiter?
Commenter la réponse de MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
0
Merci
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 :


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.
Commenter la réponse de MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
0
Merci
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 !
MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
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 ?
> MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
Je ne comprends pas de quoi tu parles pour la création des objets, objets de quoi ?
MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
Dans l'appli, on peut créer des instances de classes comme des cubes ou des cylindres pour accéder à leurs propriétés, les manipuler ou les modéliser :
> MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
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.
MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
-
En fait, NyanMaths, c'est mon appli : - D

Bon, je pense que je vais devoir faire des bidouillages avec les fonctions exec () et eval () dans une interface graphique pour permettre à l'utilisateur d'éviter de se perdre, même si je voulais cette application plus "libre" et moins lourde, mais je pense que c'est la meilleure solution.

A moins que vous n'ayez une autre idée à poser sur la table ?
Merci déjà pour votre aide ! ; - )
Commenter la réponse de MemeTech
Messages postés
52
Date d'inscription
mercredi 14 août 2019
Statut
Membre
Dernière intervention
19 novembre 2019
0
Merci
Bon, après un certain nombre de tests et de bidouillages, j'ai l'impression que tout est ok !
Je mets le sujet en résolu dans deux jours si tout va bien et si personne n'a d'idées en plus pour améliorer la chose.

Merci encore pour votre aide !
Commenter la réponse de MemeTech