浅談(x,y)=(y,x)
4050 ワード
2つの変数の値を交換します.最も一般的な書き方は次のとおりです.
しかし、実はもっとPythonicの書き方はこうです.
なぜPythonでこのように2つの変数の値を交換できるのか考えたことがありますか?
Pythonコードは、先に解釈(ここでの解釈はコンパイルに対して、PythonはC/C++のようなコンパイル言語とは異なり、ソースファイルからマシン命令にコンパイルする必要がある)からPythonバイトコード(byte code,.pycファイルは主にこれらのバイトコードを格納するために使用される)になった後、Python解釈器によってこれらのバイトコードを実行する.一般的に、Python文はいくつかのバイトコード命令に対応し、Pythonのバイトコードはアセンブリ命令の中間言語に似ているが、1つのバイトコードは1つのマシン指定に対応するだけではない.
内蔵モジュール
The dis module supports the analysis of CPython bytecode by disassembling it. The CPython bytecode which this module takes as an input is defined in the file Include/opcode.h and used by the compiler and the interpreter.
常用
dis.dis([bytesource])Disassemble the bytesource object. bytesource can denote either a module, a class, a method, a function, or a code object. For a module, it disassembles all functions. For a class, it disassembles all methods. For a single code sequence, it prints one line per bytecode instruction. If no object is provided, it disassembles the last traceback.
dis.dis受信パラメータは、モジュール、クラス、方法、関数、またはオブジェクトであってもよいコードブロックであり、このコードブロックに対応するバイトコード命令シーケンスを得ることができる.
出力のフォーマットは、行番号、アドレス、命令、操作パラメータ、パラメータ解釈(変数名、定数値などを識別)です.
本題に入り、2番目の書き方のバイトコード命令を直接見てみましょう.
swap_2.py
python -m dis swap_2.py
一部のバイトコード命令は以下の通りです.具体的な命令は公式サイトに移動してください.
LOAD_CONST(consti)Pushes co_consts[consti] onto the stack.STORE_NAME(namei)Implements name = TOS. namei is the index of name in the attribute co_names of the code object. The compiler tries to use STORE_FAST or STORE_GLOBAL if possible.LOAD_NAME(namei)Pushes the value associated with co_names[namei] onto the stack.ROT_TWO()Swaps the two top-most stack items.
上記のバイトコード命令を説明します.
1行目は
2行目の実行方法は1行目と同じです.
ポイント3行目、前の2行の計算順序はいずれも友から左へ(一般的にPython式の計算順序は左から右ですが、式の付与時には式の右のオペランドが左より優先されます)、つまり4行目はこのように実行され、まずメタグループ(y,x)を作成し、実行する動作は2つの
次の実行命令から分かるように、まずco_を取得します.names[0]の変数名xは,スタックトップ要素(現在は元のyの値)がスタックから出て格納され,2回の格納で2つの変数の値を交換することを実現する.
第2の方法は、任意の中間変数を用いず、より良い性能を得ることができる.簡単にテストできます.
なぜ2つ目の方法で消費する時間が少ないのでしょうか.中間変数の付与による時間の消費であると推測できる.具体的な検証は、次の2つの方法のバイトコード命令を解析することができる.
Life such short,be Pythonic .
Blog : JunNplus
>>> temp = x
>>> x = y
>>> y = temp
しかし、実はもっとPythonicの書き方はこうです.
>>> x, y = y, x
なぜPythonでこのように2つの変数の値を交換できるのか考えたことがありますか?
Pythonコードは、先に解釈(ここでの解釈はコンパイルに対して、PythonはC/C++のようなコンパイル言語とは異なり、ソースファイルからマシン命令にコンパイルする必要がある)からPythonバイトコード(byte code,.pycファイルは主にこれらのバイトコードを格納するために使用される)になった後、Python解釈器によってこれらのバイトコードを実行する.一般的に、Python文はいくつかのバイトコード命令に対応し、Pythonのバイトコードはアセンブリ命令の中間言語に似ているが、1つのバイトコードは1つのマシン指定に対応するだけではない.
内蔵モジュール
dis
は、バイトコードを解析するために使用することができる.DOC The dis module supports the analysis of CPython bytecode by disassembling it. The CPython bytecode which this module takes as an input is defined in the file Include/opcode.h and used by the compiler and the interpreter.
常用
dis
モジュール方法:dis.dis([bytesource]) dis.dis([bytesource])Disassemble the bytesource object. bytesource can denote either a module, a class, a method, a function, or a code object. For a module, it disassembles all functions. For a class, it disassembles all methods. For a single code sequence, it prints one line per bytecode instruction. If no object is provided, it disassembles the last traceback.
dis.dis受信パラメータは、モジュール、クラス、方法、関数、またはオブジェクトであってもよいコードブロックであり、このコードブロックに対応するバイトコード命令シーケンスを得ることができる.
>>> import dis
>>> def test():
... a = 1
...
...
>>> dis.dis(test)
3 0 LOAD_CONST 1 (1)
3 STORE_FAST 0 (a)
6 LOAD_CONST 0 (None)
9 RETURN_VALUE
出力のフォーマットは、行番号、アドレス、命令、操作パラメータ、パラメータ解釈(変数名、定数値などを識別)です.
本題に入り、2番目の書き方のバイトコード命令を直接見てみましょう.
swap_2.py
x = 1
y = 3
x, y = y, x
python -m dis swap_2.py
1 0 LOAD_CONST 0 (1)
3 STORE_NAME 0 (x)
2 6 LOAD_CONST 1 (3)
9 STORE_NAME 1 (y)
3 12 LOAD_NAME 1 (y)
15 LOAD_NAME 0 (x)
18 ROT_TWO
19 STORE_NAME 0 (x)
22 STORE_NAME 1 (y)
25 LOAD_CONST 2 (None)
28 RETURN_VALUE
一部のバイトコード命令は以下の通りです.具体的な命令は公式サイトに移動してください.
LOAD_CONST(consti)Pushes co_consts[consti] onto the stack.STORE_NAME(namei)Implements name = TOS. namei is the index of name in the attribute co_names of the code object. The compiler tries to use STORE_FAST or STORE_GLOBAL if possible.LOAD_NAME(namei)Pushes the value associated with co_names[namei] onto the stack.ROT_TWO()Swaps the two top-most stack items.
上記のバイトコード命令を説明します.
1行目は
LOAD_CONST
とSTORE_NAME
の2バイトコード命令を実行し、実行する動作はco_consts[0]スタック(すなわち定数テーブルの最初の定数、整数1がスタックに押し込まれた)を押してco_を取得するnames[0]の変数名x(変数名テーブルの最初の名前)、スタックトップ要素(整数1)スタックとco_names[0]はf->f_に格納されるlocals. 2行目の実行方法は1行目と同じです.
co_consts[0] = 1
co_names[0] = x
f->f_locals['x'] = 1
co_consts[1] = 3
co_names[1] = y
f->f_locals['y'] = 3
ポイント3行目、前の2行の計算順序はいずれも友から左へ(一般的にPython式の計算順序は左から右ですが、式の付与時には式の右のオペランドが左より優先されます)、つまり4行目はこのように実行され、まずメタグループ(y,x)を作成し、実行する動作は2つの
LOAD_NAME
で、local,global,builtin名前空間のco_を順に検索しますnames[1](対応変数名y)とco_names[0](対応変数名x)は、対応する値をスタックに圧縮する.次に実行される動作は、交換ROT_TWO
であり、交換スタックの上部の2つの要素位置である.次の実行命令から分かるように、まずco_を取得します.names[0]の変数名xは,スタックトップ要素(現在は元のyの値)がスタックから出て格納され,2回の格納で2つの変数の値を交換することを実現する.
第2の方法は、任意の中間変数を用いず、より良い性能を得ることができる.簡単にテストできます.
>>> from timeit import Timer
>>> Timer('temp = x;x = y;y = temp', 'x=2;y=3').timeit()
0.030814170837402344
>>> Timer('x, y = y, x', 'x=2;y=3').timeit()
0.027340173721313477
なぜ2つ目の方法で消費する時間が少ないのでしょうか.中間変数の付与による時間の消費であると推測できる.具体的な検証は、次の2つの方法のバイトコード命令を解析することができる.
Life such short,be Pythonic .
Blog : JunNplus