Python 3でのプロジェクトの移植の経験

プロジェクトをPython 2.7からPython 3.5に移植した経験を共有したいと思います。 異常な待ち伏せやその他の興味深いニュアンス。



プロジェクトについて少し:







移植は2to3ユーティリティを使用して実行され、その後のテストの復元が行われました。 これにどれだけ時間がかかったかを言うのは困難です;プロジェクト-趣味-私は自由な時間にそれをします。



2to3
2to3は、Python 2のソースをPython 3の外観に変換します。 これを行うために、彼女は一連のヒューリスティックを適用します(リストをカスタマイズできます)。 一般に、ユーティリティに問題はありませんでしたが、大規模および/または複雑なプロジェクトがある場合は、開始する前にヒューリスティックのリストをよく理解することをお勧めします。



ソースを処理した後、パフォーマンスを変換の最前線に置くものではないため、変更を差し引くことを強くお勧めします。



また、一部の名前が削除された/変更可能なメソッドと重複する可能性があります。 たとえば、2to3は、自分のクラスのhas_keyメソッドで機能するコードを変更しました(Python 2辞書にはこのメソッドがあり、Python 3では削除されました)。



進歩の代償



だから、もしあなたがPython 3に向かって進歩を始めたなら、あなたは何につまずくことができるでしょう。私は最も興味深いものから始めます。



銀行の丸め



「CHEEEEEGOOOO?!?」O_O


次のテストを整理しているときに、コンソールで次のように表示されたとき、これはほぼ私の反応でした。



round(1.5) 2 round(2.5) 2
      
      





「バンキング」丸め-最も近い偶数への丸め。 これらは、「学校」の切り上げを置き換える新しい丸めルールです。



「バンキング」丸めの意味は、大量のデータや複雑な計算を扱う場合、エラーが蓄積する可能性を減らすことです。 通常の「学校」の丸めとは異なり、常に半値から大きな数になります。



ほとんどの場合、この変更は重要ではありませんが、プログラムの動作にまったく予期しない変更を引き起こす可能性があります。 私の場合、たとえば、ゲームマップ上の道路の場所が変更されました。



どんな精度でも機能することに注意してください。
 round(1.65, 1) 1.6 round(1.55, 1) 1.6
      
      





整数除算が小数になりました



タイプintの整数演算に依存している場合( 1/4 == 0



場合)、コードの長い減算の準備ができています。現在1/4 == 0.25



あり、 /



//



整数除算演算子)による/



自動置換は機能しません。 -変数のタイプに関する情報が不足しているため。



Guido van Rossum はこの変更の理由を詳細に説明しました。



新しいマップセマンティクス



複数のシーケンスの反復中のマップ関数の動作が変更されました。





Python 2:



 map(lambda x, y: (x, y), [1, 2], [1]) [(1, 1), (2, None)]
      
      





Python 3:



 list(map(lambda x, y: (x, y), [1, 2], [1])) [(1, 1)]
      
      





クラスジェネレーターおよびリスト式でクラス属性を使用することはできません



以下のコードはPython 2で動作しますが、 NameError: name 'x' is not defined



例外をスローしますNameError: name 'x' is not defined



Python 3でNameError: name 'x' is not defined



れてNameError: name 'x' is not defined







 class A(object): x = 5 y = [x for i in range(1)]
      
      





これは、ジェネレーター、リスト式、およびクラスのスコープの変更によるものです。 Stackoverflowの詳細な分析



ただし、次のコードは機能します。



 def make_y(x): return [x for i in range(1)] class A(object): x = 5 y = make_y(x)
      
      





標準クラスの新しいリモートメソッド



特定の名前のメソッドの有無に依存している場合、予期しない問題が発生する可能性があります。 たとえば、黒オオカミが発生していたある場所では、 __iter__



メソッドの存在によってリストと行を区別しました。 Python 2では文字列に含まれていませんが、Python 3では出現してコードが壊れました。



操作のセマンティクスがより厳密になりました



Python 2でデフォルトで機能していた一部の操作が、 Python 3で機能しなくなりました。 特に、明示的に指定された比較メソッドなしでオブジェクトを比較することは禁止されています。



object() < object()









標準クラスの実装の変更



さまざまなものがあると思いますが、辞書の動作の変更に直面しました。 次のコードは、Python 2とPython 3で異なる効果があります。



 D = {'a': 1, 'b': 2, 'c': 3} print(list(D.values()))
      
      





Python 2では、常に[1, 3, 2]



1、3、2 [1, 3, 2]



(または特定のマシン上の特定のPythonアセンブリに対して少なくとも同じシーケンス)を出力します。



Python 3では、要素のシーケンスは起動するたびに異なります。 したがって、この「機能」に依存するコードの実行結果は異なります。



もちろん、辞書の要素の固定シーケンスに特に依存していませんでしたが、判明したように、暗黙的にそれを行いました。



メモリとプロセッサの使用量



残念ながら、移植、新しいサーバーへの移行、リファクタリングの組み合わせにより、特定の測定を行うことはできませんでした。



結論



私の主な結論は、Pythonがより慣用的になったということです。





コードでは、昔は長年にわたって隠されていたセマンティックエラーを検出することが容易になりました。



2番目の結論:数学演算に縛られている場合は、20年目まで移動を遅らせる場合でも、Python 3の正しい方法ですぐに実装を開始することをお勧めします。



__future__を使用してPython 2でコードを記述すれば、移動しても問題はありません。



All Articles