目次
Python バイトコード設計
バイトコード拡張パラメータ

Python仮想マシンの使い方

May 15, 2023 pm 07:31 PM
python

Python バイトコード設計

Python バイトコードは主に 2 つの部分で構成され、1 つはオペレーション コード、もう 1 つはオペレーション コードのパラメータです。cpython では一部のバイトコードのみがパラメータを持ちます。 bytecode にはパラメータがないため、oparg の値は 0 に等しくなります。cpython では、opcode

Python仮想マシンの使い方

opcode と oparg はそれぞれ 1 バイトを占有し、cpython 仮想マシンはバイトコードを保存するためにリトル エンディアン モードを使用します。

最初にバイトコードの設計を理解するために、次のコード スニペットを使用します:

import dis


def add(a, b):
    return a + b


if __name__ == '__main__':
    print(add.__code__.co_code)
    print("bytecode: ", list(bytearray(add.__code__.co_code)))
    dis.dis(add)
ログイン後にコピー

Python3.9 での上記のコードの出力は次のとおりです:

b'|\x00|\x01\x17\x00S\x00'
bytecode:  [124, 0, 124, 1, 23, 0, 83, 0]
  5           0 LOAD_FAST                0 (a)
              2 LOAD_FAST                1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE
ログイン後にコピー

最初に必要なもの私が理解しているのは、add.__code__.co_code は関数 add のバイトコードであり、バイト シーケンスです。list(bytearray(add.__code__.co_code)) はこのシーケンスをバイトごとに結合することです。それを分割して 10 進数に変換します。先ほど説明した各命令によると、バイトコードは 2 バイトを占めるため、上記のバイトコードには 4 つの命令があります:
Python仮想マシンの使い方

オペコードと対応する操作 命令の最後には詳細な対応表があります。記事。上記のコードでは、主に 124、23、83 の 3 つのバイトコード命令が使用されています。それらに対応する演算命令は、それぞれ LOAD_FAST、BINARY_ADD、RETURN_VALUE です。それらの意味は次のとおりです:

LOAD_FAST: varnames[var_num] をスタックの最上位にプッシュします。 BINARY_ADD: スタックから 2 つのオブジェクトをポップし、それらの加算結果をスタックの先頭にプッシュします。 RETURN_VALUE: スタックの先頭にある要素をポップし、関数の戻り値として使用します。

最初に知っておく必要があるのは、BINARY_ADD と RETURN_VALUE です。これら 2 つの演算命令にはパラメータがないため、これら 2 つのオペコードの後のパラメータはすべて 0 です。

しかし、LOAD_FAST にはパラメータがあります。LOAD_FAST が co-varnames[var_num] をスタックにプッシュすることはすでにわかっていますが、var_num は命令 LOAD_FAST のパラメータです。上記のコードには、a と b をスタックにプッシュする 2 つの LOAD_FAST 命令があります。varname の添字はそれぞれ 0 と 1 なので、オペランドは 0 と 1 になります。

バイトコード拡張パラメータ

上で説明した Python バイトコード オペランドとオペコードはそれぞれ 1 バイトを占有しますが、varname または定数テーブル データの数が 1 バイトより大きい場合、表現範囲が 1 バイトである場合、どうやって対処すればいいのでしょうか?

この問題を解決するために、cpython はバイトコードの拡張パラメーターを設計します。たとえば、定数テーブルに添字 66113 を持つオブジェクトをロードする場合、対応するバイトコードは次のようになります:

[144, 1, 144, 2, 100, 65]
ログイン後にコピー

144 は EXTENDED_ARG を表しますが、これは本質的に Python 仮想マシンによって実行される必要があるバイトコードではありません。このフィールドは主に拡張パラメーターの計算のために設計されています。

100 に対応する演算命令は LOAD_CONST で、その演算コードは 65 です。ただし、上記の命令では、定数テーブルの添字 65 を持つオブジェクトはロードされませんが、添字 66113 を持つオブジェクトがロードされます。理由は EXTENDED_ARG のためです。

ここで、上記の分析プロセスをシミュレートしましょう:

最初にバイトコード命令を読み取ります。オペコードは 144 に等しく、これは拡張パラメータであることを示します。次に、この時点のパラメータ arg は等しいです。から (1 x (1 << 8)) = 256。 2 番目のバイトコード命令を読み取ります。オペコードは 144 に等しく、これは拡張パラメータであることを示します。前の arg がすでに存在し、0 に等しくないため、この時点で arg の計算方法が変更されています。arg = arg < ;< 8 2 << 8 、つまり、元の arg に 256 を乗算し、新しいオペランドに 256 を乗算します。この時点では、arg = 66048 です。 3 番目のバイトコード命令を読み取ります。オペコードは 100 に等しく、これは LOAD_CONST 命令です。このときのオペコードは arg = 65 に等しくなります。オペコードは EXTENDED_ARG ではないため、オペランドを 256 で乗算する必要はありません。 。

上記の計算処理をプログラムコードで表すと以下のようになります. 以下のコードでは、コードは実バイト列 HAVE_ARGUMENT = 90 になります。

def _unpack_opargs(code):
    extended_arg = 0
    for i in range(0, len(code), 2):
        op = code[i]
        if op >= HAVE_ARGUMENT:
            arg = code[i+1] | extended_arg
            extended_arg = (arg << 8) if op == EXTENDED_ARG else 0
        else:
            arg = None
        yield (i, op, arg)
ログイン後にコピー

コードを使用して以前の分析を検証できます:

import dis


def num_to_byte(n):
    return n.to_bytes(1, "little")


def nums_to_bytes(data):
    ans = b"".join([num_to_byte(n) for n in data])
    return ans


if __name__ == &#39;__main__&#39;:
    # extended_arg extended_num opcode oparg for python_version > 3.5
    bytecode = nums_to_bytes([144, 1, 144, 2, 100, 65])
    print(bytecode)
    dis.dis(bytecode)
ログイン後にコピー

上記のコードの出力結果は次のとおりです:

b&#39;\x90\x01\x90\x02dA&#39;
          0 EXTENDED_ARG             1
          2 EXTENDED_ARG           258
          4 LOAD_CONST           66113 (66113)
ログイン後にコピー

出力結果に従って確認できます。上記のプログラムの分析は正しいことが判明しました。

ソース コード バイトコード マッピング テーブル

このセクションでは、主にコード オブジェクト オブジェクト内の co_lnotab フィールドを分析し、特定のフィールドを分析することでこのフィールドの設計を学習します。

import dis


def add(a, b):
    a += 1
    b += 2
    return a + b


if __name__ == &#39;__main__&#39;:
    dis.dis(add.__code__)
    print(f"{list(bytearray(add.__code__.co_lnotab)) = }")
    print(f"{add.__code__.co_firstlineno = }")
ログイン後にコピー

まず、dis の出力の 1 列目はバイトコードに対応するソース コードの行番号、2 列目はバイト シーケンス内のバイトコードのディスプレースメントです。

上記のコードの出力結果は次のとおりです。

  源代码的行号  字节码的位移
  6           0 LOAD_FAST                0 (a)
              2 LOAD_CONST               1 (1)
              4 INPLACE_ADD
              6 STORE_FAST               0 (a)

  7           8 LOAD_FAST                1 (b)
             10 LOAD_CONST               2 (2)
             12 INPLACE_ADD
             14 STORE_FAST               1 (b)

  8          16 LOAD_FAST                0 (a)
             18 LOAD_FAST                1 (b)
             20 BINARY_ADD
             22 RETURN_VALUE
list(bytearray(add.__code__.co_lnotab)) = [0, 1, 8, 1, 8, 1]
add.__code__.co_firstlineno = 5
ログイン後にコピー

上記のコードの出力結果から、バイトコードが 3 つのセグメントに分割されており、各セグメントがバイトコードを表していることがわかります。 1 行のコードのバイトコード。次に、co_lnotab フィールドを分析してみましょう。このフィールドは実際には 2 バイトに分割されています。たとえば、上記の [0, 1, 8, 1, 8, 1] は、[0, 1]、[8, 1]、[8, 1] の 3 つのセグメントに分割できます。意味は次のとおりです:

第一个数字表示距离上一行代码的字节码数目。 第二个数字表示距离上一行有效代码的行数。

现在我们来模拟上面代码的字节码的位移和源代码行数之间的关系:

[0, 1],说明这行代码离上一行代码的字节位移是 0 ,因此我们可以看到使用 dis 输出的字节码 LOAD_FAST ,前面的数字是 0,距离上一行代码的行数等于 1 ,代码的第一行的行号等于 5,因此 LOAD_FAST 对应的行号等于 5 + 1 = 6 。 [8, 1],说明这行代码距离上一行代码的字节位移为 8 个字节,因此第二块的 LOAD_FAST 前面是 8 ,距离上一行代码的行数等于 1,因此这个字节码对应的源代码的行号等于 6 + 1 = 7。 [8, 1],同理可以知道这块字节码对应源代码的行号是 8 。

现在有一个问题是当两行代码之间相距的行数超过 一个字节的表示范围怎么办?在 python3.5 以后如果行数差距大于 127,那么就使用 (0, 行数) 对下一个组合进行表示,(0, \(x_1\)), (0,$ x_2$) ... ,直到 \(x_1 + ... + x_n\) = 行数。

在后面的程序当中我们会使用 compile 这个 python 内嵌函数。当你使用Python编写代码时,可以使用compile()函数将Python代码编译成字节代码对象。这个字节码对象可以被传递给Python的解释器或虚拟机,以执行代码。

compile()函数接受三个参数:

source: 要编译的Python代码,可以是字符串,字节码或AST对象。 filename: 代码来源的文件名(如果有),通常为字符串。 mode: 编译代码的模式。可以是 'exec'、'eval' 或 'single' 中的一个。'exec' 模式用于编译多行代码,'eval' 用于编译单个表达式,'single' 用于编译单行代码。

import dis

code = """
x=1
y=2
""" \
+ "\n" * 500 + \
"""
z=x+y
"""

code = compile(code, &#39;<string>&#39;, &#39;exec&#39;)
print(list(bytearray(code.co_lnotab)))
print(code.co_firstlineno)
dis.dis(code)
ログイン後にコピー

上面的代码输出结果如下所示:

[0, 1, 4, 1, 4, 127, 0, 127, 0, 127, 0, 121]
1
  2           0 LOAD_CONST               0 (1)
              2 STORE_NAME               0 (x)

  3           4 LOAD_CONST               1 (2)
              6 STORE_NAME               1 (y)

505           8 LOAD_NAME                0 (x)
             10 LOAD_NAME                1 (y)
             12 BINARY_ADD
             14 STORE_NAME               2 (z)
             16 LOAD_CONST               2 (None)
             18 RETURN_VALUE
ログイン後にコピー

根据我们前面的分析因为第三行和第二行之间的差距大于 127 ,因此后面的多个组合都是用于表示行数的。

505 = 3(前面已经有三行了) + (127 + 127 + 127 + 121)(这个是第二行和第三行之间的差距,这个值为 502,中间有 500 个换行但是因为字符串相加的原因还增加了两个换行,因此一共是 502 个换行)。

具体的算法用代码表示如下所示,下面的参数就是我们传递给 dis 模块的 code,也就是一个 code object 对象。

def findlinestarts(code):
    """Find the offsets in a byte code which are start of lines in the source.

    Generate pairs (offset, lineno) as described in Python/compile.c.

    """
    byte_increments = code.co_lnotab[0::2]
    line_increments = code.co_lnotab[1::2]
    bytecode_len = len(code.co_code)

    lastlineno = None
    lineno = code.co_firstlineno
    addr = 0
    for byte_incr, line_incr in zip(byte_increments, line_increments):
        if byte_incr:
            if lineno != lastlineno:
                yield (addr, lineno)
                lastlineno = lineno
            addr += byte_incr
            if addr >= bytecode_len:
                # The rest of the lnotab byte offsets are past the end of
                # the bytecode, so the lines were optimized away.
                return
        if line_incr >= 0x80:
            # line_increments is an array of 8-bit signed integers
            line_incr -= 0x100
        lineno += line_incr
    if lineno != lastlineno:
        yield (addr, lineno)
ログイン後にコピー
操作 操作码
POP_TOP 1
ROT_TWO 2
ROT_THREE 3
DUP_TOP 4
DUP_TOP_TWO 5
ROT_FOUR 6
NOP 9
UNARY_POSITIVE 10
UNARY_NEGATIVE 11
UNARY_NOT 12
UNARY_INVERT 15
BINARY_MATRIX_MULTIPLY 16
INPLACE_MATRIX_MULTIPLY 17
BINARY_POWER 19
BINARY_MULTIPLY 20
BINARY_MODULO 22
BINARY_ADD 23
BINARY_SUBTRACT 24
BINARY_SUBSCR 25
BINARY_FLOOR_DIVIDE 26
BINARY_TRUE_DIVIDE 27
INPLACE_FLOOR_DIVIDE 28
INPLACE_TRUE_DIVIDE 29
RERAISE 48
WITH_EXCEPT_START 49
GET_AITER 50
GET_ANEXT 51
BEFORE_ASYNC_WITH 52
END_ASYNC_FOR 54
INPLACE_ADD 55
INPLACE_SUBTRACT 56
INPLACE_MULTIPLY 57
INPLACE_MODULO 59
STORE_SUBSCR 60
DELETE_SUBSCR 61
BINARY_LSHIFT 62
BINARY_RSHIFT 63
BINARY_AND 64
BINARY_XOR 65
BINARY_OR 66
INPLACE_POWER 67
GET_ITER 68
GET_YIELD_FROM_ITER 69
PRINT_EXPR 70
LOAD_BUILD_CLASS 71
YIELD_FROM 72
GET_AWAITABLE 73
LOAD_ASSERTION_ERROR 74
INPLACE_LSHIFT 75
INPLACE_RSHIFT 76
INPLACE_AND 77
INPLACE_XOR 78
INPLACE_OR 79
LIST_TO_TUPLE 82
RETURN_VALUE 83
IMPORT_STAR 84
SETUP_ANNOTATIONS 85
YIELD_VALUE 86
POP_BLOCK 87
POP_EXCEPT 89
STORE_NAME 90
DELETE_NAME 91
UNPACK_SEQUENCE 92
FOR_ITER 93
UNPACK_EX 94
STORE_ATTR 95
DELETE_ATTR 96
STORE_GLOBAL 97
DELETE_GLOBAL 98
LOAD_CONST 100
LOAD_NAME 101
BUILD_TUPLE 102
BUILD_LIST 103
BUILD_SET 104
BUILD_MAP 105
LOAD_ATTR 106
COMPARE_OP 107
IMPORT_NAME 108
IMPORT_FROM 109
JUMP_FORWARD 110
JUMP_IF_FALSE_OR_POP 111
JUMP_IF_TRUE_OR_POP 112
JUMP_ABSOLUTE 113
POP_JUMP_IF_FALSE 114
POP_JUMP_IF_TRUE 115
LOAD_GLOBAL 116
IS_OP 117
CONTAINS_OP 118
JUMP_IF_NOT_EXC_MATCH 121
SETUP_FINALLY 122
LOAD_FAST 124
STORE_FAST 125
DELETE_FAST 126
RAISE_VARARGS 130
CALL_FUNCTION 131
MAKE_FUNCTION 132
BUILD_SLICE 133
LOAD_CLOSURE 135
LOAD_DEREF 136
STORE_DEREF 137
DELETE_DEREF 138
CALL_FUNCTION_KW 141
CALL_FUNCTION_EX 142
SETUP_WITH 143
LIST_APPEND 145
SET_ADD 146
MAP_ADD 147
LOAD_CLASSDEREF 148
EXTENDED_ARG 144
SETUP_ASYNC_WITH 154
FORMAT_VALUE 155
BUILD_CONST_KEY_MAP 156
BUILD_STRING 157
LOAD_METHOD 160
CALL_METHOD 161
LIST_EXTEND 162
SET_UPDATE 163
DICT_MERGE 164
DICT_UPDATE 165

以上がPython仮想マシンの使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

C言語合計の機能は何ですか? C言語合計の機能は何ですか? Apr 03, 2025 pm 02:21 PM

C言語に組み込みの合計機能はないため、自分で書く必要があります。合計は、配列を通過して要素を蓄積することで達成できます。ループバージョン:合計は、ループとアレイの長さを使用して計算されます。ポインターバージョン:ポインターを使用してアレイ要素を指し示し、効率的な合計が自己概要ポインターを通じて達成されます。アレイバージョンを動的に割り当てます:[アレイ]を動的に割り当ててメモリを自分で管理し、メモリの漏れを防ぐために割り当てられたメモリが解放されます。

独特の目標は関連していますか? 独特の目標は関連していますか? Apr 03, 2025 pm 10:30 PM

明確で明確なものは区別に関連していますが、それらは異なる方法で使用されます。明確な(形容詞)は、物事自体の独自性を説明し、物事の違いを強調するために使用されます。明確な(動詞)は、区別の動作または能力を表し、差別プロセスを説明するために使用されます。プログラミングでは、個別は、重複排除操作などのコレクション内の要素の独自性を表すためによく使用されます。明確なは、奇数や偶数の偶数を区別するなど、アルゴリズムまたは関数の設計に反映されます。最適化する場合、異なる操作は適切なアルゴリズムとデータ構造を選択する必要がありますが、異なる操作は、論理効率の区別を最適化し、明確で読み取り可能なコードの書き込みに注意を払う必要があります。

誰がより多くのPythonまたはJavaScriptを支払われますか? 誰がより多くのPythonまたはJavaScriptを支払われますか? Apr 04, 2025 am 12:09 AM

スキルや業界のニーズに応じて、PythonおよびJavaScript開発者には絶対的な給与はありません。 1. Pythonは、データサイエンスと機械学習でさらに支払われる場合があります。 2。JavaScriptは、フロントエンドとフルスタックの開発に大きな需要があり、その給与もかなりです。 3。影響要因には、経験、地理的位置、会社の規模、特定のスキルが含まれます。

Cで理解する方法!x? Cで理解する方法!x? Apr 03, 2025 pm 02:33 PM

!X理解!Xは、C言語の論理的な非操作者です。 Xの値をブーリングします。つまり、虚偽の真の変化、trueへの誤った変更です。ただし、Cの真実と虚偽はブール型ではなく数値で表されていることに注意してください。非ゼロは真であると見なされ、0のみが偽と見なされます。したがって、!xは正の数と同じ負の数を扱い、真実と見なされます。

c言語でsumとはどういう意味ですか? c言語でsumとはどういう意味ですか? Apr 03, 2025 pm 02:36 PM

Cには組み込みの合計関数はありませんが、次のように実装できます。ループを使用して要素を1つずつ蓄積します。ポインターを使用して、要素に1つずつアクセスして蓄積します。大量のデータ量については、並列計算を検討してください。

H5ページの生産には継続的なメンテナンスが必要ですか? H5ページの生産には継続的なメンテナンスが必要ですか? Apr 05, 2025 pm 11:27 PM

H5ページは、コードの脆弱性、ブラウザー互換性、パフォーマンスの最適化、セキュリティの更新、ユーザーエクスペリエンスの改善などの要因のため、継続的に維持する必要があります。効果的なメンテナンス方法には、完全なテストシステムの確立、バージョン制御ツールの使用、定期的にページのパフォーマンスの監視、ユーザーフィードバックの収集、メンテナンス計画の策定が含まれます。

ラブコードのコピーをコピーして貼り付けて無料でラブコードを貼り付けます ラブコードのコピーをコピーして貼り付けて無料でラブコードを貼り付けます Apr 04, 2025 am 06:48 AM

コードのコピーと貼り付けは不可能ではありませんが、注意して扱う必要があります。コード内の環境、ライブラリ、バージョンなどの依存関係は、現在のプロジェクトと一致しないため、エラーや予測不可能な結果が得られます。ファイルパス、従属ライブラリ、Pythonバージョンなど、コンテキストが一貫していることを確認してください。さらに、特定のライブラリのコードをコピーして貼り付けるときは、ライブラリとその依存関係をインストールする必要がある場合があります。一般的なエラーには、パスエラー、バージョンの競合、一貫性のないコードスタイルが含まれます。パフォーマンスの最適化は、コードの元の目的と制約に従って再設計またはリファクタリングする必要があります。コピーされたコードを理解してデバッグすることが重要であり、盲目的にコピーして貼り付けないでください。

58.com作業ページでリアルタイムアプリケーションと視聴者のデータを取得する方法は? 58.com作業ページでリアルタイムアプリケーションと視聴者のデータを取得する方法は? Apr 05, 2025 am 08:06 AM

クロール中に58.com作業ページの動的データを取得するにはどうすればよいですか? Crawlerツールを使用して58.comの作業ページをrawったら、これに遭遇する可能性があります...

See all articles