21

関数 types.CodeType() を使用して新しいコード オブジェクトを作成したいと思います。
これに関するドキュメントはほとんどなく、既存のドキュメントには「気弱な人向けではない」と書かれ
ています。必要なものを教えて、 types.CodeType に渡される各引数に関する情報を教えてください

:
通常の使用例では、組み込み関数 compile() だけが必要です
。types.CodeType() は、通常のソース コードでは取得できず、バイトコードへの直接アクセスが必要な新しい命令を作成する場合にのみ使用してください。

4

2 に答える 2

43

––––––––––<br> 免責事項:
この回答のドキュメントは公式のものではなく、間違っている可能性があります。

この回答は、python バージョン 3.x でのみ有効です。

–––––––––––</p>

コード オブジェクトを作成するには、関数 CodeType() に次の引数を渡す必要があります。

CodeType(
        argcount,             #   integer
        kwonlyargcount,       #   integer
        nlocals,              #   integer
        stacksize,            #   integer
        flags,                #   integer
        codestring,           #   bytes
        consts,               #   tuple
        names,                #   tuple
        varnames,             #   tuple
        filename,             #   string
        name,                 #   string
        firstlineno,          #   integer
        lnotab,               #   bytes
        freevars,             #   tuple
        cellvars              #   tuple
        )

ここで、各引数の意味を説明しようと思います。

argcount
関数に渡される引数の数 (*args と **kwargs は含まれません)。

kwonlyargcountキーワードのみの引数
の数。

nlocals
ローカル変数の数。
つまり、グローバル名を除くすべての変数とパラメーター (*args と **kwargs を含む)。

stacksize コードが必要とするスタック (仮想マシン スタック) の量。
それがどのように機能するかを理解したい場合は、公式ドキュメントを参照してください。

flags
コード オブジェクトについて何かを示すビットマップ:
1 –> コードが最適化された
2 –> newlocals: 新しいローカル名前空間 (関数など) がある
4 –> コードは任意の数の位置引数を受け入れる (*args は使用)
8 –> コードは任意の数のキーワード引数を受け入れます (*kwargs が使用されます)
32 –> コードはジェネレーターです

その他のフラグは、古い python バージョンで使用されているか、__ future __ からインポートされたものを示すためにアクティブ化されています

codestring 理解を深めたい場合は
、バイトコード命令を表す一連のバイト。ドキュメントを参照してください(上記と同じ) 。

consts
バイトコードで使用されるリテラルを含むタプル (事前に計算された数値、タプル、文字列など)

バイトコードで使用される名前
を含むタプル。
この名前は、グローバル変数、関数、クラス、またはオブジェクトからロードされた属性です。

varnames
バイトコードで使用されるローカル名を含むタプル (最初に引数、次にローカル変数)

filename
コードがコンパイルされたファイル名です。
それはあなたが望むものである可能性があり、あなたはこれについて自由に嘘をつくことができます. ;)

name
関数の名前を与えます。また、これは好きなように指定できますが、注意してください:
これはトレースバックに表示される名前です。名前が不明な場合、トレースバックが不明になる
可能性があります。

firstlineno
関数の最初の行 (ソース コードをコンパイルした場合のデバッグ用)

lnotab
バイトコードのオフセットを行番号に関連付けるバイトのマッピング。
(これもデバッグ目的だと思いますが、これに関するドキュメントはほとんどありません)

freevars
自由変数の名前を含むタプル。
フリー変数は、コード オブジェクトが定義された名前空間で宣言された変数であり、ネストされた関数が宣言されたときに使用されます。
これはモジュール レベルでは発生しません。その場合、自由変数はグローバル変数でもあるためです。

cellvars
ネストされた関数によって参照されるローカル変数の名前を含むタプル。

––––––––––––<br> :
次の例は、上で述べたことの意味を明確にするはずです。

: 上記の完成したコード オブジェクトの属性にはco_プレフィックスが
あり、関数は実行可能な本体を__code__属性に格納します。

––––––––––––<br> 1番目の例

def F(a,b):
    global c
    k=a*c
    w=10
    p=(1,"two",3)

print(F.__code__.co_argcount)
print(F.__code__.co_nlocals , F.__code__.co_varnames)
print(F.__code__.co_stacksize)
print(F.__code__.co_flags)
print(F.__code__.co_names)
print(F.__code__.co_consts)

出力:

2
5 ('a', 'b', 'k', 'w', 'p')
3
67
('c' ,)
(None, 10, 1, 'two'. 3, (1, 'two', 3))
  1. この関数には 2 つの引数が渡されます ("a"、"b")

  2. この関数には、2 つのパラメーター ("a"、"b") と 3 つのローカル変数 ("k"、"w"、"p") があります。

  3. 関数のバイトコードを逆アセンブルすると、次のようになります。

    3         0 LOAD_FAST                0 (a)             #stack:  ["a"] 
              3 LOAD_GLOBAL              0 (c)             #stack:  ["a","c"]
              6 BINARY_MULTIPLY                            #stack:  [result of a*c]
              7 STORE_FAST               2 (k)             #stack:  []
    
    4        10 LOAD_CONST               1 (10)            #stack:  [10]
             13 STORE_FAST               3 (w)             #stack:  []
    
    5        16 LOAD_CONST               5 ((1, 'two', 3)) #stack:  [(1,"two",3)]
             19 STORE_FAST               4 (p)             #stack:  []
             22 LOAD_CONST               0 (None)          #stack:  [None]
             25 RETURN_VALUE                               #stack:  []
    

    chile が関数を実行していることに気付くと思いますが、スタック内の要素が 3 つを超えることはありません (この場合、タプルはその長さとしてカウントされます)。

  4. フラグの値はdec 67 = bin 1000011 = bin 1000000 +10 +1 = dec 64 +2 +1 なので、

    • コードが最適化されている (自動生成されたコードのほとんどがそうであるように)
    • 関数バイトコードのローカル名前空間の変更の実行中に
    • 64? 実際、私はその意味が何であるかを知りません
  5. 関数で使用される唯一のグローバル名は "c" で、co_names に格納されます。

  6. 使用するすべての明示的なリテラルは co_consts に格納されます。

    • None は関数の戻り値です
    • w に数値 10 を明示的に割り当てます。
    • (1, 'two', 3) を p に明示的に代入します
    • タプルが定数の場合、そのタプルの各要素は定数であるため、1、"2"、3 は定数です。

––––––––––––<br> 2 番目の例

ModuleVar="hi"

def F():
    FunctionVar=106
    UnusedVar=ModuleVar

    def G():
        return (FunctionVar,ModuleVar)

    print(G.__code__.co_freevars)
    print(G.__code__.co_names)

F()
print(F.__code__.co_cellvars)
print(F.__code__.co_freevars)
print(F.__code__.co_names)

出力:

('FunctionVar',)
('ModuleVar',)
('FunctionVar',)
()
('print', '__code__', 'co_freevars', 'co_names', 'ModuleVar')

出力の意味は次のとおりです。

1 行目と 2 行目は F が実行されたときに表示されるため、G コードの co_freevars と co_names が表示さ

ます。グローバルとして。

次の 3 行は、F コードの co_cellvars、co_freevars、および co_names 属性に関するものです。
「FunctionVar」は G のネストされた関数で参照されるため、cellvar としてマークされ、
「ModuleVar」は F が作成された名前空間にありますが、モジュール変数な
ので、freevar としてマークされませんが、グローバル名で見つかります。
また、組み込み関数 print は names でマークされ、F で使用される属性のすべての名前がマークされます。

––––––––––––<br> 3番目の例

これは動作するコード オブジェクトの初期化です。
これは役に立ちませんが、この関数で必要なことはすべて実行できます。

MyCode= CodeType(
        0,
        0,
        0,
        3,
        64,
        bytes([101, 0, 0,    #Load print function
               101, 1, 0,    #Load name 'a'
               101, 2, 0,    #Load name 'b'
               23,           #Take first two stack elements and store their sum
               131, 1, 0,    #Call first element in the stack with one positional argument
               1,            #Pop top of stack
               101, 0, 0,    #Load print function
               101, 1, 0,    #Load name 'a'
               101, 2, 0,    #Load name 'b'
               20,           #Take first two stack elements and store their product
               131, 1, 0,    #Call first element in the stack with one positional argument
               1,            #Pop top of stack
               100, 0, 0,    #Load constant None
               83]),         #Return top of stack
        (None,),
        ('print', 'a', 'b'),
        (),
        'PersonalCodeObject',
        'MyCode',
        1,
        bytes([14,1]),
        (),
        () )

a=2
b=3
exec(MyCode) # code prints the sum and the product of "a" and "b"

出力:

5
6
于 2013-04-20T17:26:21.850 に答える
5

CodeType コンストラクターの使用例は、標準ライブラリ、具体的には Lib/modulefinder.py にあります。そこを見るとco_filename、ファイル内のすべてのコード オブジェクトの読み取り専用属性を再定義するために使用されていることがわかります。

私は最近、関数ファクトリがある同様のユースケースに遭遇しましたが、生成された関数は常にトレースバックで「汎用」名を持っていたため、目的の名前を含むようにコード オブジェクトを再生成する必要がありました。

>>> def x(): raise NotImplementedError
...
>>> x.__name__
'x'
>>> x.__name__ = 'y'
>>> x.__name__
'y'
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in x
NotImplementedError

>>> x.__code__.co_name
'x'
>>> x.__code__.__name__ = 'y'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: readonly attribute

>>> 'Gah!'
'Gah!'

しかし、待ってください、関数の__code__メンバーは読み取り専用ではないので、モジュールファインダーが行うことを行うことができます:

>>> from types import CodeType
>>> co = x.__code__
>>> x.__code__ = CodeType(co.co_argcount, co.co_kwonlyargcount,
             co.co_nlocals, co.co_stacksize, co.co_flags,
             co.co_code, co.co_consts, co.co_names,
             co.co_varnames, co.co_filename,
             'MyNewCodeName',
             co.co_firstlineno, co.co_lnotab, co.co_freevars,
             co.co_cellvars)
>>> x.__code__.co_name
'MyNewCodeName'
>>> x()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in MyNewCodeName
NotImplementedError

この例で注意すべき点は、スタック トレースで値を生成するときに、トレースバックがco_name属性ではなく属性を使用することです。func.__name__

もう 1 つ注意: 上記は Python 3 です。Python 2 と互換性を持たせるには、コンストラクター ( co_kwonlyargcount) の 2 番目の引数を省略します。

更新: Victor Stinner は、Python 3.8 の CodeType クラスに新しいメソッド「replace」を追加しました。これにより、状況が大幅に簡素化されます。これは、将来の互換性の問題を排除するために行われました。3.8 では、'co_argcount' の後に新しい 'co_posonlyargcount' 引数もコール リストに追加されたため、少なくとも 3.8 以降のコードは、引数リストが再び変更された場合にある程度将来的に保証されます。

>>> x.__code__ = x.__code__.replace(co_name='MyNewCodeName')
于 2016-10-16T00:31:31.443 に答える