Python-future: Mauvaise conversion de la division en old_div

Créé le 30 janv. 2019  ·  12Commentaires  ·  Source: PythonCharmers/python-future

Exemple de code

Le code suivant contient une division de style ancien.

(x / 2 * 3.0)

Le résultat attendu

Le résultat de l'exécution du code ci-dessus avec des valeurs arbitraires affectées à x doit être le suivant, où les parenthèses indiquent explicitement l'ordre des opérations (x / 2) * 3.0 . Ou écrit dans un autre ordre, le résultat doit être évalué comme x * (3.0 / 2) .

Le problème

Une fois que nous avons exécuté futurize sur le code ci-dessus, un old_div est inséré par l'appareil fix_division_safe . Cependant, comme on peut le voir dans le diff ci-dessous, la fonction est appelée avec un mauvais ordre d'arguments.

$ futurize --stage2 src/example.py
RefactoringTool: Refactored src/example.py
--- src/example.py      (original)
+++ src/example.py      (refactored)
@@ -1 +1,3 @@
-(x / 2 * 3.0)
+from __future__ import division
+from past.utils import old_div
+(old_div(x, 2 * 3.0))
RefactoringTool: Files that need to be modified:
RefactoringTool: src/example.py

Conversion attendue

Comme déjà indiqué dans la section « résultat attendu », la conversion correcte aurait dû être : (old_div(x, 2) * 3.0)

0.18 bug

Commentaire le plus utile

J'ai aussi ce problème, en exécutant futurize 0.17.1 sur Python 2.7.16. Cet exemple illustre le problème :

$ echo 'x = 100/10 * 10' | futurize --stage2 -

RefactoringTool: Refactored <stdin>
--- <stdin> (original)
+++ <stdin> (refactored)
@@ -1 +1,3 @@
-x = 100/10 * 10
+from __future__ import division
+from past.utils import old_div
+x = old_div(100,10 * 10)
RefactoringTool: Files that need to be modified:
RefactoringTool: <stdin>

Notez que 100/10 * 10 (==100) est converti en old_div(100,10 * 10) (==1). Cela me semble être un bug majeur.

Tous les 12 commentaires

Ou écrit dans un autre ordre, le résultat doit être évalué comme x * (3.0 / 2) .

Bien que mathématiquement équivalent, ce n'est pas égal sur Python 2. Si x est un int , alors x / 2 sera tronqué en premier dans certains cas, alors que (3.0 / 2) sera toujours être 1,5 et ne jamais tronquer.

Bien sûr, cela ne change pas le bug.

Je le pensais aussi, c'est pourquoi j'ai utilisé un Python 2.7.15 ainsi que Python 3.7.1 pour exécuter l'exemple et valider qu'ils présentent le même comportement. Il est bon d'entendre que ma première intention n'était pas complètement fausse.

Peut-être qu'il est également connecté à l'OS? J'exécute l'exemple sur Windows 7 x64.

Pouvez-vous donner un exemple dans quels cas Python 2 évaluerait l'expression d'une autre manière ?

>>> y = range(10)

>>> [x / 2 * 3.0 for x in y]
[0.0, 0.0, 3.0, 3.0, 6.0, 6.0, 9.0, 9.0, 12.0, 12.0]

>>> [x * (3.0 / 2) for x in y]
[0.0, 1.5, 3.0, 4.5, 6.0, 7.5, 9.0, 10.5, 12.0, 13.5]

(Votre conversion attendue est bien sûr le résultat correct.)

Tout à fait ce à quoi je m'attendais. Cependant, j'ai explicitement testé en utilisant 1 pour x et j'ai obtenu 1,5 en conséquence.

J'ai également oublié de mentionner qu'il y a une sortie différente du code si vous n'utilisez pas les parenthèses. Dans ce cas, futurize donnera la conversion correcte.

1 / 2 * 3.0 - - > old_div(1, 2) * 3.0

J'ai rencontré le même bug et je travaille sur le correctif sur ma branche .
Il semble que mes modifications résolvent ce problème comme suit :

$ cat sample.py          
x =  10
(x / 2 * 3.0)

Sortie d'origine

$ futurize sample.py 
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored sample.py
--- sample.py   (original)
+++ sample.py   (refactored)
@@ -1,2 +1,4 @@
+from __future__ import division
+from past.utils import old_div
 x =  10
-(x / 2 * 3.0)
+(old_div(x, 2 * 3.0))
RefactoringTool: Files that need to be modified:
RefactoringTool: sample.py

Nouvelle sortie

$ futurize sample.py
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored sample.py
--- sample.py   (original)
+++ sample.py   (refactored)
@@ -1,2 +1,4 @@
+from __future__ import division
+from past.utils import old_div
 x =  10
-(x / 2 * 3.0)
+(old_div(x, 2) * 3.0)
RefactoringTool: Files that need to be modified:
RefactoringTool: sample.py

Je vais créer une pull request quand je vois le temps ..

Voici d'autres exemples.

J'ai également oublié de mentionner qu'il y a une sortie différente du code si vous n'utilisez pas les parenthèses. Dans ce cas, futurize donnera la conversion correcte.

1 / 2 * 3.0 - - > old_div(1, 2) * 3.0

Contrairement à ce que vous avez dit, je vois que 1 / 2 * 3.0 est converti en old_div(1, 2 * 3.0) manière incorrecte en utilisant le code actuel sur master..

$ cat sample2.py
1 / 2 * 3.0
x =  10
x / 2
x / 2.0
x / 2 * 3
x / (2 * 3)
x * 2 / 3

Sortie d'origine

$ futurize sample2.py
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored sample2.py
--- sample2.py  (original)
+++ sample2.py  (refactored)
@@ -1,7 +1,9 @@
-1 / 2 * 3.0
+from __future__ import division
+from past.utils import old_div
+old_div(1, 2 * 3.0)
 x =  10
-x / 2
+old_div(x, 2)
 x / 2.0
-x / 2 * 3
-x / (2 * 3)
-x * 2 / 3
+old_div(x, 2 * 3)
+old_div(x, (2 * 3))
+old_div(x * 2, 3)
RefactoringTool: Files that need to be modified:
RefactoringTool: sample2.py

Nouvelle sortie

$ futurize sample2.py
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored sample2.py
--- sample2.py  (original)
+++ sample2.py  (refactored)
@@ -1,7 +1,9 @@
-1 / 2 * 3.0
+from __future__ import division
+from past.utils import old_div
+old_div(1, 2) * 3.0
 x =  10
-x / 2
+old_div(x, 2)
 x / 2.0
-x / 2 * 3
-x / (2 * 3)
-x * 2 / 3
+old_div(x, 2) * 3
+old_div(x, (2 * 3))
+old_div(x * 2, 3)
RefactoringTool: Files that need to be modified:
RefactoringTool: sample2.py

PR créé (https://github.com/PythonCharmers/python-future/pull/441)

J'ai également observé que le old_div est inséré même lorsque les opérandes gauche et droit sont des flottants, par exemple

1.0/1024.0

devient

old_div(1.0,1024.0)

Je pense que cette conversion est inutile car la division où les deux opérandes sont des flottants produit le même résultat, à la fois en Python 2 et Python 3.

@mattgathu Quelle version de futurize et Python utilisez-vous ? Avec Python 3.7.2 et futurize 0.17.1 (le dernier de PyPI), 1.0/1024.0 reste intact lorsque futurize d.

$ python --version
Python 3.7.2
$ futurize --version
0.17.1

$ cat test.py
1.0 / 1024.0
1   / 1024.0
1.0 / 1024

$ futurize test.py
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: No files need to be modified.

Voici mes versions :

$ python -V
Python 2.7.16
$ futurize --version
0.16.0

J'ai mis à niveau vers 0.17.0 et il ne semble pas avoir le problème. Merci

En effet, il semble que la v0.17.0 inclut une amélioration pour ignorer les conversions inutiles.

https://github.com/PythonCharmers/python-future/releases/tag/v0.17.0

Corrigez fix_division_safe pour prendre en charge une meilleure conversion des expressions complexes et ignorer la division flottante évidente.

J'ai aussi ce problème, en exécutant futurize 0.17.1 sur Python 2.7.16. Cet exemple illustre le problème :

$ echo 'x = 100/10 * 10' | futurize --stage2 -

RefactoringTool: Refactored <stdin>
--- <stdin> (original)
+++ <stdin> (refactored)
@@ -1 +1,3 @@
-x = 100/10 * 10
+from __future__ import division
+from past.utils import old_div
+x = old_div(100,10 * 10)
RefactoringTool: Files that need to be modified:
RefactoringTool: <stdin>

Notez que 100/10 * 10 (==100) est converti en old_div(100,10 * 10) (==1). Cela me semble être un bug majeur.

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

jackjansen picture jackjansen  ·  7Commentaires

wimglenn picture wimglenn  ·  5Commentaires

asottile picture asottile  ·  18Commentaires

foreignmeloman picture foreignmeloman  ·  3Commentaires

e-rk picture e-rk  ·  14Commentaires