如何创建在Python代码对象?(How to create a code object in py

2019-08-31 09:20发布

我想创建一个新的代码对象与功能types.CodeType()。
有关于这几乎没有任何文档和现有的人说“不适合心脏微弱”
告诉我我需要什么,给我约传递给types.CodeType每个参数的一些信息,
可能张贴的例子。

注意
在正常使用情况下,你只需要内置函数编译()
您应该使用types.CodeType()只如果你想创建一个不能获得正常书写源代码的新的指令和要求,以字节码的直接访问。

Answer 1:

-----------
免责声明
在这个答案的文档是不是官方的,可能是不正确的。

这个答案只适用于蟒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
数只有关键字的参数 。

nlocals
局部变量的数目,
即所有变量和参数(*指定参数和** kwargs包括在内),除了全局名称。

STACKSIZE堆的由代码所需要的量(虚拟机堆),
如果你想了解它是如何工作的,请参阅官方文档 。


位图,说一些有关的代码对象:
1 - >代码进行了优化
2 - > newlocals:有一个新的本地名称空间中(例如,函数)
4 - >的代码接受的位置参数的任意数量(* ARGS使用)
8 - >的代码接受的keyworded参数的任意数量(* kwargs使用)
32 - >的代码是发电机

othes标志在老的Python版本中使用或激活该说些什么,从未来 __ __进口

码流
表示字节码指令的字节序列
如果你想有一个更好的了解,请参阅文档 (同上)

consts
一种包含由字节码中使用文字元组(例如预先计算的数字,元组和字符串)


使用字节码含有名称元组
这个名字是全局变量,函数和类或者也可在对象属性中加载

varnames
一种包含由字节码中使用本地名称元组(第一参数,然后局部变量)

文件名
它是从哪个编译代码的文件名。
它可以是任何你想要的,你可以自由地撒谎这一点。 ;)

名称
它给出了函数的名称。 这也可能是你想要的,但要注意:
这是在回溯中显示的名称,如果名称不明确的,回溯可能是不清楚,
只是想想如何lambda表达式可以是恼人。

firstlineno
该函数的第一行(用于调试的目的,如果你编译源代码)

lnotab
字节的映射该相关的字节码偏移量行号。
(我想这也就是为调试目的,有几个文档关于本)

freevars
含自由变量的名称元组。
自由变量是在命名空间声明的变量,其中被定义的代码对象,当嵌套函数被声明它们被使用;
这并不在模块级发生的,因为在这种情况下自由变量也是全局变量。

cellvars
一个包含嵌套函数引用的局部变量的名称元组。

------------
例子
下面的例子应该澄清上面已经说的意思。

:在完成代码对象上面提到的有CO_前缀属性,
和存储功能的可执行体在__code__属性

------------
第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. 有传递给此函数(“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,所以我们明白

    • 的代码被优化(如大部分的自动生成的代码的是)
    • 在执行功能的字节码局部命名空间的变化
    • 64? 其实我也不知道什么是它的意义
  5. 这是在函数中使用的全球唯一的名称是“C”,它存储在co_names

  6. 我们利用每一个明确的文字保存在co_consts:

    • 都不是函数的返回值
    • 我们在10号明确指定为w
    • 我们明确地分配(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如果第一和第二线被印刷,因此它们显示出co_freevars和的G代码co_names:
“FunctionVar”是在F函数的名称空间,其中G被创建,
“ModuleVar”,而不是一个模块变量,因此它被认为是全球性的。

以下三条线约co_cellvars,co_freevars和F的代码co_names属性:
“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"

输出:

5
6


Answer 2:

所述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__成员不是只读的,所以我们可以做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 )。



文章来源: How to create a code object in python?