Python-future: 除算のold_divへの誤った変換

作成日 2019年01月30日  ·  12コメント  ·  ソース: PythonCharmers/python-future

サンプルコード

次のコードには、古いスタイルの分割が含まれています。

(x / 2 * 3.0)

期待される結果

xに任意の値を割り当てて上記のコードを実行すると、次のようになります。括弧は、操作(x / 2) * 3.0の順序を明示的に示しています。 または、別の順序で書き込むと、結果はx * (3.0 / 2)として評価されます。

問題

上記のコードでfuturizeを実行すると、 old_divfix_division_safeフィクスチャによって挿入されます。 ただし、以下の差分でわかるように、関数は間違った引数の順序で呼び出されます。

$ 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

期待される変換

「期待される結果」のセクションですでに述べたように、正しい変換は次のようになっているはずです: (old_div(x, 2) * 3.0)

0.18 bug

最も参考になるコメント

私もこの問題を抱えており、Python2.7.16でfuturize0.17.1を実行しています。 この例は、問題を示しています。

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

100/10 * 10 (== 100)はold_div(100,10 * 10) (== 1)に変換されることに注意してください。 これは私には大きなバグのようです。

全てのコメント12件

または、別の順序で書き込むと、結果はx * (3.0 / 2)として評価されます。

数学的には同等ですが、Python 2では等しくありません。 xint場合、 x / 2が最初に切り捨てられる場合がありますが、 (3.0 / 2)は切り捨てられます。常に1.5であり、切り捨てることはありません。

もちろん、それはバグを変更しません。

私もそう思ったので、Python2.7.15とPython3.7.1を使用して例を実行し、それらが同じ動作を示すことを検証しました。 私の最初の意図が完全に間違っていなかったと聞いてうれしいです。

多分それはOSにも接続されていますか? この例をWindows7x64で実行しています。

Python 2が別の方法で式を評価する場合の例を挙げていただけますか?

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

(もちろん、期待される変換は正しい結果です。)

完全に私が期待していたもの。 ただし、xに1を使用して明示的にテストした結果、1.5が得られました。

また、括弧を使用しない場合、コードの出力が異なることにも言及するのを忘れました。 この場合、futurizeは正しい変換を生成します。

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

同じバグが発生し、ブランチの修正に取り組んでいます。
私の変更により、この問題は次のように修正されるようです。

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

元の出力

$ 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

新しい出力

$ 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

時間が表示されたら、プルリクエストを作成します。

その他の例を次に示します。

また、括弧を使用しない場合、コードの出力が異なることにも言及するのを忘れました。 この場合、futurizeは正しい変換を生成します。

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

あなたが言ったこととは反対に、マスターの現在のコードを使用して、 1 / 2 * 3.0old_div(1, 2 * 3.0)誤って変換されているのがわかります。

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

元の出力

$ 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

新しい出力

$ 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が作成されました(https://github.com/PythonCharmers/python-future/pull/441)

また、左右のオペランドが両方とも浮動小数点である場合でも、 old_divが挿入されることも確認しました。

1.0/1024.0

になります

old_div(1.0,1024.0)

Python2とPython3の両方で、両方のオペランドがfloatである除算では同じ結果が生成されるため、この変換は不要だと思います。

@mattgathuどのバージョンのfuturizeとPythonを使用していますか? Pythonの3.7.2とfuturize 0.17.1(は、PyPIの最新)、と1.0/1024.0滞在無傷で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.

これが私のバージョンです:

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

0.17.0にアップグレードし

実際、v0.17.0には、不要な変換をスキップする機能拡張が含まれているようです。

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

fix_division_safeを修正して、複雑な式のより良い変換をサポートし、明らかな浮動小数点除算をスキップします。

私もこの問題を抱えており、Python2.7.16でfuturize0.17.1を実行しています。 この例は、問題を示しています。

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

100/10 * 10 (== 100)はold_div(100,10 * 10) (== 1)に変換されることに注意してください。 これは私には大きなバグのようです。

このページは役に立ちましたか?
0 / 5 - 0 評価