Python-future: Conversão errada de divisão para div_antigo

Criado em 30 jan. 2019  ·  12Comentários  ·  Fonte: PythonCharmers/python-future

Código de exemplo

O código a seguir contém uma divisão de estilo antigo.

(x / 2 * 3.0)

O resultado esperado

O resultado da execução do código acima com valores arbitrários atribuídos ax deve ser o seguinte, onde os parênteses mostram explicitamente a ordem das operações (x / 2) * 3.0 . Ou escrito em outra ordem, o resultado deve ser avaliado como x * (3.0 / 2) .

O problema

Uma vez que executamos futurize no código acima, um old_div é inserido pelo fixture fix_division_safe . No entanto, como se pode ver no diff abaixo, a função é chamada com uma ordem incorreta de argumentos.

$ 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

Conversão esperada

Conforme já declarado na seção 'resultado esperado', a conversão correta deveria ter sido: (old_div(x, 2) * 3.0)

0.18 bug

Comentários muito úteis

Também tenho esse problema ao executar futurize 0.17.1 no Python 2.7.16. Este exemplo ilustra o problema:

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

Observe que 100/10 * 10 (== 100) é convertido em old_div(100,10 * 10) (== 1). Isso me parece um grande bug.

Todos 12 comentários

Ou escrito em outra ordem, o resultado deve ser avaliado como x * (3.0 / 2) .

Embora matematicamente equivalente, não é igual no Python 2. Se x for int , então x / 2 truncará primeiro em alguns casos, enquanto (3.0 / 2) irá sempre seja 1.5 e nunca truncar.

Embora, é claro, isso não mude o bug.

Também pensei assim, por isso usei um Python 2.7.15 e também um Python 3.7.1 para executar o exemplo e validar se eles apresentam o mesmo comportamento. É bom saber que minha primeira intenção não foi completamente errada.

Talvez também esteja conectado ao sistema operacional? Estou executando o exemplo no Windows 7 x64.

Você pode dar um exemplo em que casos Python 2 avaliaria a expressão de outra maneira?

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

(Sua conversão esperada é, obviamente, o resultado correto.)

Totalmente o que eu esperava. No entanto, testei explicitamente usando 1 para x e obtive 1,5 como resultado.

Também esqueci de mencionar que há uma saída diferente do código se você não usar os parênteses. Neste caso, futurize produzirá a conversão correta.

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

Encontrei o mesmo bug e estou trabalhando na correção do meu branch .
Parece que minhas alterações corrigem esse problema da seguinte maneira:

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

Saída original

$ 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

Nova saída

$ 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

Vou criar uma solicitação pull quando tiver tempo ..

Aqui estão mais exemplos.

Também esqueci de mencionar que há uma saída diferente do código se você não usar os parênteses. Neste caso, futurize produzirá a conversão correta.

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

Ao contrário do que você disse, vejo que 1 / 2 * 3.0 foi convertido em old_div(1, 2 * 3.0) incorretamente usando o código atual no mestre.

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

Saída original

$ 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

Nova saída

$ 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 criado (https://github.com/PythonCharmers/python-future/pull/441)

Também observei que old_div é inserido mesmo quando os operandos esquerdo e direito são flutuantes, por exemplo

1.0/1024.0

torna-se

old_div(1.0,1024.0)

Acho que essa conversão é desnecessária, pois a divisão em que os dois operandos são flutuantes produz o mesmo resultado, tanto no Python 2 quanto no Python 3.

@mattgathu Qual versão do futurize e Python você usa? Com Python 3.7.2 e futurize 0.17.1 (o mais recente em PyPI), 1.0/1024.0 permanece intacto quando 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.

Aqui estão minhas versões:

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

Eu atualizei para 0.17.0 e parece não ter o problema. Obrigado

Na verdade, parece que a v0.17.0 inclui um aprimoramento para pular conversões desnecessárias.

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

Corrija fix_division_safe para oferecer suporte a uma melhor conversão de expressões complexas e pular a divisão de flutuação óbvia.

Também tenho esse problema ao executar futurize 0.17.1 no Python 2.7.16. Este exemplo ilustra o problema:

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

Observe que 100/10 * 10 (== 100) é convertido em old_div(100,10 * 10) (== 1). Isso me parece um grande bug.

Esta página foi útil?
0 / 5 - 0 avaliações