地獄のパイソン







最近、面白い、トリッキー、そして奇妙なJavaScriptの例について書きました 。 今ではPythonの番です。 高度なインタープリター型言語であるPythonには、多くの便利なプロパティがあります。 しかし、一見したところ一部のコードの結果が明らかでない場合があります。







以下は、Pythonでの予期しない動作の例を収集し、内部で何が起こるかを議論する楽しいプロジェクトです。 いくつかの例は、実際のWTFのカテゴリに属していません!?、しかし、それらはあなたが避けたい言語の興味深い特徴を示しています。 これはPythonの内部の仕組みを学ぶ良い方法だと思います。興味を持っていただければ幸いです。







既に経験豊富なPythonプログラマーである場合、多くの例はあなたによく知られているかもしれませんし、それらに頭を悩ませるとき、それらの場合に懐かしささえ引き起こすかもしれません:)







内容





例の構造



注:上記の例はすべて、対話型Python 3.5.2インタープリターでテストされており、説明で特に指定されていない限り、すべてのバージョンの言語で動作するはずです。







例の構造:







いくつかの愚かな見出し



 # . #   ...
      
      





結果(Pythonバージョン):







 >>> _ ,  
      
      





(オプション):予期しない結果の1行の説明。







説明









PSコマンドラインでこれらの例を読むこともできます。 wtfpython



wtfpython



npmパッケージをインストールするだけで、







 $ npm install -g wtfpython
      
      





コマンドラインでwtfpython



を実行すると、このコレクションが$PAGER



開きます。







#TODO:pypiパッケージを追加して、コマンドラインで読み取ります。









行スキップ?



結果:







 >>> value = 11 >>> valu = 32 >>> value 11
      
      





ワット?







注:この例を再現する最も簡単な方法は、ファイル/シェルにコピーして貼り付けることです。







説明







一部のUnicode文字はASCIIと同じように見えますが、インタープリターによって異なります。







 >>> value = 42 #ascii e >>> valu = 23 #cyrillic e, Python 2.x interpreter would raise a `SyntaxError` here >>> value 42
      
      





まあ、どういうわけか怪しい...



 def square(x): """        . """ sum_so_far = 0 for counter in range(x): sum_so_far = sum_so_far + x return sum_so_far
      
      





結果(Python 2.x):







 >>> square(10) 10
      
      





100ではないでしょうか?

注:結果を再現できない場合は、シェルでmixed_tabs_and_spaces.pyファイルを実行してみてください。







説明









結果(Python 3.x):







 TabError: inconsistent use of tabs and spaces in indentation
      
      





ハッシュケーキの時間!



1。







 some_dict = {} some_dict[5.5] = "Ruby" some_dict[5.0] = "JavaScript" some_dict[5] = "Python"
      
      





結果:







 >>> some_dict[5.5] "Ruby" >>> some_dict[5.0] "Python" >>> some_dict[5] "Python"
      
      





PythonはJavaScriptの存在を破壊しましたか?







説明









処理時間の不一致



 array = [1, 8, 15] g = (x for x in array if array.count(x) > 0) array = [2, 8, 22]
      
      





結果:







 >>> print(list(g)) [8]
      
      





説明









反復中に辞書を変換する



 x = {0: None} for i in x: del x[i] x[i+1] = None print(i)
      
      





結果:







 0 1 2 3 4 5 6 7
      
      





はい、 8回実行されて停止します。







説明









反復中にリストアイテムを削除する



 list_1 = [1, 2, 3, 4] list_2 = [1, 2, 3, 4] list_3 = [1, 2, 3, 4] list_4 = [1, 2, 3, 4] for idx, item in enumerate(list_1): del item for idx, item in enumerate(list_2): list_2.remove(item) for idx, item in enumerate(list_3[:]): list_3.remove(item) for idx, item in enumerate(list_4): list_4.pop(idx)
      
      





結果:







 >>> list_1 [1, 2, 3, 4] >>> list_2 [2, 4] >>> list_3 [] >>> list_4 [2, 4]
      
      





結果が得られた理由を知っていますか[2, 4]









説明









なぜそれが起こったのか[2, 4]











行末のバックスラッシュ



結果:







 >>> print("\\ some string \\") >>> print(r"\ some string") >>> print(r"\ some string \") File "<stdin>", line 1 print(r"\ some string \") ^ SyntaxError: EOL while scanning string literal
      
      





説明









巨大な糸を作ろう!



これはまったくWTFではありませんが、いくつかのクールなことだけであり、注意する必要があります:)







 def add_string_with_plus(iters): s = "" for i in range(iters): s += "xyz" assert len(s) == 3*iters def add_string_with_format(iters): fs = "{}"*iters s = fs.format(*(["xyz"]*iters)) assert len(s) == 3*iters def add_string_with_join(iters): l = [] for i in range(iters): l.append("xyz") s = "".join(l) assert len(s) == 3*iters def convert_list_to_string(l, iters): s = "".join(l) assert len(s) == 3*iters
      
      





結果:







 >>> timeit(add_string_with_plus(10000)) 100 loops, best of 3: 9.73 ms per loop >>> timeit(add_string_with_format(10000)) 100 loops, best of 3: 5.47 ms per loop >>> timeit(add_string_with_join(10000)) 100 loops, best of 3: 10.1 ms per loop >>> l = ["xyz"]*10000 >>> timeit(convert_list_to_string(l, 10000)) 10000 loops, best of 3: 75.3 µs per loop
      
      





説明









文字列連結インタープリターの最適化



 >>> a = "some_string" >>> id(a) 140420665652016 >>> id("some" + "_" + "string") # Notice that both the ids are same. 140420665652016 # using "+", three strings: >>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100) 0.25748300552368164 # using "+=", three strings: >>> timeit.timeit("s1 += s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100) 0.012188911437988281
      
      





説明









はい、存在します!



forループのelse



。 典型的な例:







  def does_exists_num(l, to_find): for num in l: if num == to_find: print("Exists!") break else: print("Does not exist")
      
      





結果:







 >>> some_list = [1, 2, 3, 4, 5] >>> does_exists_num(some_list, 4)
      
      





あります!







 >>> does_exists_num(some_list, -1)
      
      





存在しません。







例外処理のelse



。 例:







 try: pass except: print("Exception occurred!!!") else: print("Try block executed successfully...")
      
      





結果:







 Try block executed successfully...
      
      





説明









is



そうではis



ません



この例は非常に広く知られています。







 >>> a = 256 >>> b = 256 >>> a is b True >>> a = 257 >>> b = 257 >>> a is b False >>> a = 257; b = 257 >>> a is b True
      
      





説明







is



==



の違い









 >>> [] == [] True >>> [] is [] # These are two empty lists at two different memory locations. False
      
      





256



は既存のオブジェクトですが、 257



はそうではありません


Pythonを起動すると、-5〜256の数値がメモリに割り当てられます。 これらは頻繁に使用されるため、準備を整えておくことをお勧めします。







https://docs.python.org/3/c-api/long.htmlから引用







現在の実装では、-5〜256のすべての数値に対して整数オブジェクトの配列がサポートされているため、この範囲からintを作成すると、既存のオブジェクトへのリンクが取得されます。 したがって、値を1に変更できるはずです。しかし、この場合、Pythonの動作は予測不能になると思われます。 :-)


 >>> id(256) 10922528 >>> a = 256 >>> b = 256 >>> id(a) 10922528 >>> id(b) 10922528 >>> id(257) 140084850247312 >>> x = 257 >>> y = 257 >>> id(x) 140084850247440 >>> id(y) 140084850247344
      
      





インタープリターはそれほどスマートではなく、実行時にy = 257



は値257



整数を既に作成したことを理解していなかったため、メモリ内に別のオブジェクトを作成します。







a



b



は、同じ行に同じ値を持つ初期化中に同じオブジェクトを参照します。







 >>> a, b = 257, 257 >>> id(a) 140640774013296 >>> id(b) 140640774013296 >>> a = 257 >>> b = 257 >>> id(a) 140640774013392 >>> id(b) 140640774013488
      
      







is not ...



is (not ...)





 >>> 'something' is not None True >>> 'something' is (not None) False
      
      





説明









ループ内の関数は同じ結果を返します。



 funcs = [] results = [] for x in range(7): def some_func(): return x funcs.append(some_func) results.append(some_func()) funcs_results = [func() for func in funcs]
      
      





結果:







 >>> results [0, 1, 2, 3, 4, 5, 6] >>> funcs_results [6, 6, 6, 6, 6, 6, 6]
      
      





some_func



funcs



に追加する前に、各反復のx



値が異なる場合、すべての関数は6を返しました。







 //OR >>> powers_of_x = [lambda x: x**i for i in range(10)] >>> [f(2) for f in powers_of_x] [512, 512, 512, 512, 512, 512, 512, 512, 512, 512]
      
      





説明









 funcs = [] for x in range(7): def some_func(x=x): return x funcs.append(some_func)
      
      





結果:







 >>> funcs_results = [func() for func in funcs] >>> funcs_results [0, 1, 2, 3, 4, 5, 6]
      
      





ローカルスコープからのループ変数の漏洩



1。







 for x in range(7): if x == 6: print(x, ': for x inside loop') print(x, ': x in global')
      
      





結果:







 6 : for x inside loop 6 : x in global
      
      





ただし、 x



スコープ外のループに対して定義されていません。







2。







 # This time let's initialize x first x = -1 for x in range(7): if x == 6: print(x, ': for x inside loop') print(x, ': x in global')
      
      





結果:







 6 : for x inside loop 6 : x in global
      
      





3。







 x = 1 print([x for x in range(5)]) print(x, ': x in global')
      
      





結果(Python 2.xの場合):







 [0, 1, 2, 3, 4] (4, ': x in global')
      
      





結果(Python 3.xの場合):







 [0, 1, 2, 3, 4] 1 : x in global
      
      





説明









三目並べ、最初の試行でXが勝つ



 # Let's initialize a row row = [""]*3 #row i['', '', ''] # Let's make a board board = [row]*3
      
      





結果:







 >>> board [['', '', ''], ['', '', ''], ['', '', '']] >>> board[0] ['', '', ''] >>> board[0][0] '' >>> board[0][0] = "X" >>> board [['X', '', ''], ['X', '', ''], ['X', '', '']]
      
      





しかし、3つのXは割り当てませんでしたか?







説明







この視覚化は、 row



変数が初期化されたときにメモリで何が起こるかを説明しています。







画像







board



row



を乗算して初期化されると、これはメモリ内で発生します( board[0]



board[1]



およびboard[2]



各要素は、 row



指定された同じリストへのリンクです)。







画像







可変のデフォルト引数に注意してください



 def some_func(default_arg=[]): default_arg.append("some_string") return default_arg
      
      





結果:







 >>> some_func() ['some_string'] >>> some_func() ['some_string', 'some_string'] >>> some_func([]) ['some_string'] >>> some_func() ['some_string', 'some_string', 'some_string']
      
      





説明









 def some_func(default_arg=[]): default_arg.append("some_string") return default_arg
      
      





結果:







 >>> some_func.__defaults__ #This will show the default argument values for the function ([],) >>> some_func() >>> some_func.__defaults__ (['some_string'],) >>> some_func() >>> some_func.__defaults__ (['some_string', 'some_string'],) >>> some_func([]) >>> some_func.__defaults__ (['some_string', 'some_string'],)
      
      








All Articles