Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Chainer les opérations

+3 votes

Est-il possible de faire une différence dans la programmation de add entre les deux calculs suivants?

S = P + Q + R

et

T = P + Q
S = T + R

En fait j'aimerai que pour le premier exemple, S garde en mémoire qu'il a été produit pas deux additions alors que dans le deuxième, j'aimerai que S garde en mémoire qu'il a été produit par une seule addition (T+Q).

demandé 23-Avr-2015 par sPaz (306 points)
edité 23-Avr-2015 par sPaz

3 Réponses

+5 votes

Non.

L'addition est un opérateur binaire, donc concrètement dans les deux cas S est le résultat d'UNE seule addition. Si tu voit 2 additions dans le premier cas (S=P+Q+R), c'est que l'un des termes ajouté est lui-même résultat d'une addition: S=P+(Q+R)

Autrement dit, quand on écrit

S = P + Q + R

python voit

S = P + (Q + R)

et le résout par

tmp = Q + R
S = P + tmp

Je ne connais pas ton application mais j'ai du mal à voir pourquoi on aurait besoin de faire une distinction entre tes deux cas.

répondu 23-Avr-2015 par bubulle (2,256 points)

C'est ce que j'avais compris du fonctionnement de python mais j'espérai qu'un élément m'avait échappé!

En fait, je suis en train d'écrire un "programme" qui a pour but de calculer comme un élève (c'est à dire en expliquant les étapes de calcul).
Disons que l'on ai 3 polynomes P, Q et R.

>>> P = Polynom([1,2,3])
>>> Q = Polynom([4,5,6])
>>> R = Polynom([7,8,9])

Pour le moment, mon programme sait ajouter deux à deux des polynômes tout en gardant les étapes.

>>> S1 = P + Q
>>> print(S1)
9 x ^ 2 + 7 x + 5
>>> for i in S1.explain():
...     print(i)
3 x^{  2 } + 2 x + 1 + 6 x^{  2 } + 5 x + 4
( 3 + 6 ) x^{  2 } + ( 2 + 5 ) x + 1 + 4
9 x^{  2 } + 7 x + 5

Mais maintenant si je veux enchaîner deux additions, seules les étapes de la dernière addition sont gardées

>>> S = P + Q + R
>>> print(S)
18 x^{  2 } + 15 x + 12
>>> for i in S.explain():
...     print(i)
9 x^{  2 } + 7 x + 5 + 9 x^{  2 } + 8 x + 7
( 9 + 9 ) x^{  2 } + ( 7 + 8 ) x + 5 + 7
18 x^{  2 } + 15 x + 12

Je pourrai faire ne sorte que les étapes antérieurs soient transmises lors d'un calcul. Mais cela ne me conviendrai pas non plus car une fois qu'un polynôme est défini je ne veux pas que l'on réexplique à chaque fois comment on a pu le trouver.

>>> S1 = P + Q
>>> print(S1)
9 x ^ 2 + 7 x + 5
>>> for i in S1.explain():
...     print(i)
3 x^{  2 } + 2 x + 1 + 6 x^{  2 } + 5 x + 4
( 3 + 6 ) x^{  2 } + ( 2 + 5 ) x + 1 + 4
9 x^{  2 } + 7 x + 5
>>> # On fait pleins d'autres choses. Et un moment on a besoin de refaire un calcul avec S1
>>> S = S1 + R
>>> for i n S.explain(): # Ce comportement ici me va très très bien.
...    print(i)
9 x^{  2 } + 7 x + 5 + 9 x^{  2 } + 8 x + 7
( 9 + 9 ) x^{  2 } + ( 7 + 8 ) x + 5 + 7
18 x^{  2 } + 15 x + 12

Voila pourquoi j'aurai besoin de savoir s'il est possible de faire une différence entre

>>> S = P + Q + R

et

>>> S1 = P + Q
>>> S = S1 + R

Sans définir ta propre fonction pour gérer ça je ne vois pas comment faire. Surcharger l'opérateur '+' ne fonctionnera pas puisqu'il ne gérera jamais plus de deux arguments.

Et si tu saute le premier explain(), tu veux voir quoi ?

S1 = P + Q
S = S1 + R
for i n S.explain():  # Pas de S1.explain() appelé jusqu'ici
    print(i)
# affiche explication de "S1+R"? ou bien "(P+Q)+R"?

Il affichera les explications de "S1 + R".
Mais ta remarque m'a fait pensé à un truc. Si je ne veux pas qu'une chose puisse être expliquée 2 fois mais que je veux que tout soit expliqué au moins une fois, je devrais mettre un générateur derrière tout ça. De cette façon, on pourrait attendre à ce que S.explain() donne toutes les étapes même celles venant de S1 = P + Q.
Je vais essayer tout ça!
Merci

+1 vote

Précision. C'est pas plutôt :

T = P + Q

et non :

T = P + R ?

Ou alors c'est :

S = T + R ?

Ou alors on ne parle pas du même "S". Cela dit, ça n'apporte rien à ta question.
Et j'imagine que ça n'a rien à voir avec SPQR ? ^^

Bon, pour répondre, à la question, en faisant ce qui suit (modulo une ou deux fautes de syntaxe, je ne suis pas sur un PC avec Python) :

S serait une classe comportant quelque s chose du genre :

class MaSomme():
    def __init__():
        self.value = None  # résultat de l'addition
        self.nbre_add = None  # nbre d'additions

Puis une fonction d'addition serait utilisée :

def mon_add_a_moi(mon_instance, str_addition):
    # exemple appel : mon_add_a_moi(s, "a + b +c")
    # nombre de '+' dans 'str_addition'
    mon_instance.nbre_add = str_addition.count("+")
    # on calcule la valeur
    mon_instance.value = eval(str_addition)

Et le programme principal :

# le 'main'
# on instancie notre classe à une variable 's'
s = MaSomme()
# on affecte une valeur à nos paramètres
p = 1
q = 2
r = 3
mon_add_à_moi(s, "p+q+r")
print(s.nbre_add)  # 2
print(s.value)  # 6

Ça ne mémoriserait que la dernière opération et seulement pour les "+"

Si on inclus un 't' (un int standard) :

t = p + q
mon_add_a_moi(s, "t + r")
    print(s.nbre_add)  # 1
    print(s.value)  # 6
répondu 23-Avr-2015 par Agagax (182 points)

On a dit pas d'eval! Si on ne passe plus une string mais une liste à monadda_moi le sum() de bubulle fait à nouveau l'affaire ici:

def mon_add_a_moi(mon_instance, list_addition):
    # exemple appel : mon_add_a_moi(s, [a,b,c])
    mon_instance.nbre_add = len(list_addition)-1
    mon_instance.value = sum(elmt for elmt in list_addition)

Merci c'est corrigé pour le post initial.

En fait, je ne cherche pas à écrire d'autre fonctions pour garder l'historique des opérations. Je l'ai déjà fait. Ça marche mais ce n'est pas partique. Je passe par une autre classe qui "gère" les calculs plus compliqués: Expression.

>>> une_expression = Expression([P , Q , op.add , R , op.add]) # équivalent de P + Q + R en postfix
>>> print(une_expression)
3 x^{  2 } + 2 x + 1 + 6 x^{  2 } + 5 x + 4 + 9 x^{  2 } + 8 x + 7
>>> S = une_expression.simplify()
>>> print(S)
18 x^{  2 } + 15 x + 12
>>> for i in S.explain():
...      print(i)
3 x^{  2 } + 2 x + 1 + 6 x^{  2 } + 5 x + 4 + 9 x^{  2 } + 8 x + 7
( 3 + 6 ) x^{  2 } + ( 2 + 5 ) x + 1 + 4 + 9 x^{  2 } + 8 x + 7
9 x^{  2 } + 7 x + 5 + 9 x^{  2 } + 8 x + 7
( 9 + 9 ) x^{  2 } + ( 7 + 8 ) x + 5 + 7 
18 x^{  2 } + 15 x + 12

Le soucis c'est que c'est relativement relou à écrire et que j'aimerai que la syntaxe reste le plus proche possible de la manière dont on rédige les maths.

@furankun:

mon_instance.value = sum(list_addition)

marche bien aussi sinon...

oui j'y ai pensé après :P

+1 vote

Juste histoire de me contredire. Oui c'est possible si on triche un peu...

Tricher, ça veut dire ici essayer de détecter la situation des objets 'orphelins' qui n'ont pas été affectés à un nom. L'astuce c'est d'utiliser sys.getrefcount() pour les repérer.
Le problème c'est que sys.getrefcount() n'est manifestement pas disponible pour toutes les implémentations de python et que dans mon exemple, REFCOUNT_MAGIC_NUMBER a été déterminé de manière très très empirique. Ceci me fait douter de la robustesse de l'ensemble.

Quoi qu'il en soit : défi accepté ;)

PS: je fait un gc.collect() avant le sys.getrefcount() au cas où, je suis pas sûr que ce soit nécessaire, mais ça me rassure.

import sys
import gc
import operator


REFCOUNT_MAGIC_NUMBER = 11


def is_anonymous(obj):
    gc.collect()
    return sys.getrefcount(obj) <= REFCOUNT_MAGIC_NUMBER


class OperationResults(object):
    def __init__(self, operator, *operands):
        self.operator = operator
        operands = tuple(e if is_anonymous(e) else getattr(e, 'value', e)
                         for e in operands)
        self.operands = operands

    def eval(self):
        values = [getattr(e, 'value', e) for e in self.operands]
        return self.operator(*values)

    def __repr__(self):
        return "%s%s" % (self.operator.__name__, self.operands)


class Value(object):
    def __init__(self, value):
        if isinstance(value, OperationResults):
            self.origin = value
            self.value = value.eval()
        else:
            self.origin = None
            self.value = value

    def explain(self):
        res = repr(self.value)
        if self.origin:
            res = '%s = %s' % (self.origin, res)
        return res

    def __repr__(self):
        return repr(self.origin or self.value)

    def __add__(self, other):
        return Value(OperationResults(operator.add, self, other))

    def __radd__(self, other):
        return Value(OperationResults(operator.add, other, self))

###############
a = Value(1)
b = Value(2)
c = a + b

print c+a
# affiche: add(3, 1)
print (a+b)+a
# affiche: add(add(1, 2), 1)
répondu 24-Avr-2015 par bubulle (2,256 points)
...