2013-04-17 54 views
9

我想用函數types.CodeType()創建一個新的代碼對象。
幾乎沒有關於這一點,並在文檔現有說「不適合心臟微弱」
告訴我我需要什麼,給我約傳給types.CodeType,
可能發佈一個例子中,每個參數的一些信息。如何在python中創建代碼對象?

注意
在你只需要內建函數編譯正常使用情況下()
您應該使用types.CodeType()只如果你想創建一個不能獲得正常寫入新指令源代碼並需要直接訪問字節碼。

+1

爲什麼你想呢?它可能會更容易通過其他手段來完成... – mgilson

+1

http://stackoverflow.com/questions/6612449/what-are-the-arguments-to-the-types-codetype-python-call –

+1

FWIW,如果不是語言參考提到的,這意味着它預計的參數可以實現* *依賴一個 – mgilson

回答

28

-----------
免責聲明:在這個答案
文檔並非官方和可能不正確。

這個答案只適用於Python版本3.x的

-----------

爲了創建你要傳遞給函數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
參數數目(*指定參數和** kwargs不包括在內)。

kwonlyargcount
keyword-only arguments數。

nlocals
數局部變量,
即所有變量和參數(*指定參數和** kwargs包括在內),除了全局名稱。

堆棧大小 通過代碼所需堆棧(虛擬機堆)的量,如果你想了解它是如何工作
,請參閱官方Documentation

標誌
,說一些有關的代碼對象的位圖:
1 - >代碼進行了優化
2 - > newlocals:有一個新的本地名稱空間中(例如,函數)
4 - >的代碼接受的位置參數的任意數量(* ARGS使用)
8 - >的代碼接受的keyworded參數的任意數量(* kwargs使用)
32 - >的代碼是發電機

othes標誌在老的Python版本中使用或激活該說些什麼,從__ 未來進口__

碼流
代表字節碼指令字節序列
如果你想有一個更好的瞭解,請參閱Documentation(同上)

consts
一種包含由字節碼中使用文字元組(例如預先計算的數字,元組,和〜應變GS)

名稱
一種包含由字節碼
這個名稱是全局變量,函數和類使用的名稱元組或也屬性加載的從對象

varnames
含有本地名稱元組由字節碼使用(參數第一,然後是局部變量)

文件名
這是編譯代碼的文件名。
它可以是任何你想要的,你可以自由地說謊。 ;)

name
它給出了函數的名稱。 此外,這可以是任何你想要的,但要小心:
這是在回溯中顯示的名稱,如果名稱不清楚,回溯可能不清楚,
只是想想如何lambdas可以討厭。

firstlineno
函數的第一行(用於調試目的如果編譯源代碼)

lnotab
字節的映射該相關的字節碼的偏移量的行號。
(我想這也就是爲調試目的,有關於這幾個文件)

freevars
含自由變量的名稱元組。
自由變量是在定義代碼對象的名稱空間中聲明的變量, 它們在聲明嵌套函數時使用;
這不會發生在模塊級別,因爲在這種情況下,自由變量也是全局變量。

cellvars
包含由嵌套函數引用的局部變量名稱的元組。

------------
例子
下面的例子應該澄清上面已經說的意思。

:在完成的代碼對象上述屬性具有CO_前綴,
以及功能存儲其可執行體在__code__屬性

--------- ---
第一實施例

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. 有傳遞給此函數( 「A」, 「B」)的兩個參數

  2. 這個函數有兩個參數( 「A」, 「B」)和三個局部變量( 「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: [] 
    

    ,你可以看到智利的執行,我們從來沒有在堆棧以上三個元素(因爲它在這種情況下lenght元組數)

  4. 標誌的值是功能十二月 67 = 1000011 = 百萬10 1 = 64 2 1,所以我們明白

    • 代碼被優化(如大部分的自動生成的代碼的是)
    • 在執行FUNC字節碼本地名稱空間變化
    • 64?其實我也不知道什麼是它的意義
  5. 是在函數中使用全球唯一的名稱是「C」,它是存儲在co_names

  6. 我們使用的每明確字面存儲在co_consts:

    • 沒有一個是該函數的返回值
    • 我們明確數10分配給瓦特
    • 我們明確地分配(1, '2',3)至p
    • 如果元組是一個常數,元組中的每個元素是一個常數,所以如圖1所示, 「二」,3是常數

------------
第二示例

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') 

輸出的意義是這樣的:

執行F如果

第一和第二線被印刷,因此它們顯示出的G代碼co_freevars和co_names:
「FunctionVar」是在F函數的名稱空間,其中G
「ModuleVar」改爲模塊變量,所以它被認爲是全局的。

以下三行是約co_cellvars,co_freevars和co_names F的代碼屬性:
「FunctionVar」是在G嵌套函數引用的,所以它被標記爲一個cellvar,
「ModuleVar」是命名空間中的何處F已創建,但它是一個模塊變量,因此它沒有標記爲freevar,但它在全局名稱中找到,因此它是模塊變量,

也內建函數打印被標記爲名稱以及F.

------------
第三示例

中使用的屬性的所有的名字這是一個工作代碼對象初始化,
這是無用的,但你可以用這個功能做你想做的一切。

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" 

輸出:CODETYPE構造的

5 
6 
+0

你是怎麼找到這個的?我一直在尋找大約一年的代碼對象的文檔,只知道引用它們的簡短旁註。 – tox123

+0

標誌64看起來是'NOFREE'(根據'dis.code_info'的輸出)。 –

1

實施例使用可以在標準庫中找到,特別是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__成員不是隻讀的,所以我們可以做什麼modulefinder做:

>>> 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__屬性。

還要說明一點:以上是Python 3中,使它的Python 2兼容,只留下了第二個參數的構造函數(co_kwonlyargcount)。