gpt4 book ai didi

python - 函数作为 Python 中的对象 : what exactly is stored in memory?

转载 作者:太空狗 更新时间:2023-10-29 20:13:25 25 4
gpt4 key购买 nike

我已经使用 Python 解决实际问题有一段时间了,但我仍然没有对幕后发生的事情有正确的理论理解。例如,我很难理解 Python 如何将函数视为对象。我知道函数是“函数”类的对象,带有“调用”方法,并且我知道我可以通过为它们编写“调用方法”来使我的自定义类表现得像函数。但是我无法弄清楚在创建新函数时确切地存储在内存中的内容,以及如何访问存储的信息。

为了进行实验,我编写了一个小脚本来创建许多函数对象并将它们存储在一个列表中。我注意到这个程序用了很多内存。

funct_list = []
for i in range(10000000):
def funct(n):
return n + i
funct_list.append(funct)

我的问题是:
  • 当我定义一个新的函数对象时,RAM 中究竟存储了什么?我是否存储了如何实现该功能的详细信息?
  • 如果是这样,我的函数对象是否具有允许我“检查”(或什至可能“追溯更改”)函数行为方式的属性或方法?
  • 也许我之前的问题是循环的,因为函数对象的方法本身就是函数...
  • 在我上面的代码中,一些 RAM 仅用于存储指向列表中函数对象的“指针”。 RAM 的其余部分大概用于存储有关我的函数对象实际工作方式的有趣内容。 RAM在这两个目的之间大致如何分配?
  • 假设我通过让函数做更复杂的事情来改变代码片段。我会因此使用更多的 RAM 吗? (我希望如此。但是当我通过用 1000 行垃圾填充函数体来更改函数的定义时,使用的 RAM 量似乎没有任何差异。)

  • 我很想找到一个关于这个的综合引用。但是无论我在谷歌中输入什么,我似乎都找不到我要找的东西!

    最佳答案

    函数对象的数据分为两个主要部分。由相同函数定义创建的所有函数都相同的部分存储在函数的代码对象中,而即使在从相同函数定义创建的函数之间也可以更改的部分存储在函数对象中。

    函数最有趣的部分可能是它的字节码。这是核心数据结构,它说明了执行函数时实际执行的操作。它作为字节串存储在函数的代码对象中,您可以直接检查它:

    >>> def fib(i):
    ... x, y = 0, 1
    ... for _ in range(i):
    ... x, y = y, x+y
    ... return x
    ...
    >>> fib.__code__.co_code
    b'd\x03\\\x02}\x01}\x02x\x1et\x00|\x00\x83\x01D\x00]\x12}\x03|\x02|\x01|\x02\x17\x00\x02\x00}\x01}\x02q\x1
    2W\x00|\x01S\x00'

    ...但它不是为了人类可读而设计的。

    对 Python 字节码的实现细节有足够的了解后,您可以自己解析它,但描述所有这些会花费太长时间。相反,我们将使用 dis 为我们反汇编字节码的模块:
    >>> import dis
    >>> dis.dis(fib)
    2 0 LOAD_CONST 3 ((0, 1))
    2 UNPACK_SEQUENCE 2
    4 STORE_FAST 1 (x)
    6 STORE_FAST 2 (y)

    3 8 SETUP_LOOP 30 (to 40)
    10 LOAD_GLOBAL 0 (range)
    12 LOAD_FAST 0 (i)
    14 CALL_FUNCTION 1
    16 GET_ITER
    >> 18 FOR_ITER 18 (to 38)
    20 STORE_FAST 3 (_)
    4 22 LOAD_FAST 2 (y)
    24 LOAD_FAST 1 (x)
    26 LOAD_FAST 2 (y)
    28 BINARY_ADD
    30 ROT_TWO
    32 STORE_FAST 1 (x)
    34 STORE_FAST 2 (y)
    36 JUMP_ABSOLUTE 18
    >> 38 POP_BLOCK
    5 >> 40 LOAD_FAST 1 (x)
    42 RETURN_VALUE

    此处的输出中有许多列,但我们最感兴趣的是带有 ALL_CAPS 的列及其右侧的列。

    ALL_CAPS 列显示函数的字节码指令。例如, LOAD_CONST加载一个常数值,和 BINARY_ADD是用 + 添加两个对象的指令.带有数字的下一列用于字节码参数。例如, LOAD_CONST 3表示在代码对象的常量中的索引 3 处加载常量。这些总是整数,它们与字节码指令一起打包到字节码字符串中。最后一列主要提供了字节码参数的人类可读解释,例如,说 LOAD_CONST 3 中的 3对应于常数 (0, 1) ,或者说 1STORE_FAST 1对应局部变量 x .此列中的信息实际上并非来自字节码字符串;它通过检查代码对象的其他部分来解决。

    函数对象的其余数据主要是解析字节码参数所需的内容,如函数的闭包或其全局变量 dict,以及仅存在于内省(introspection)的内容,如函数的 __name__ .

    如果我们看一下 Python 3.6 function object struct definition C级:

    typedef struct {
    PyObject_HEAD
    PyObject *func_code; /* A code object, the __code__ attribute */
    PyObject *func_globals; /* A dictionary (other mappings won't do) */
    PyObject *func_defaults; /* NULL or a tuple */
    PyObject *func_kwdefaults; /* NULL or a dict */
    PyObject *func_closure; /* NULL or a tuple of cell objects */
    PyObject *func_doc; /* The __doc__ attribute, can be anything */
    PyObject *func_name; /* The __name__ attribute, a string object */
    PyObject *func_dict; /* The __dict__ attribute, a dict or NULL */
    PyObject *func_weakreflist; /* List of weak references */
    PyObject *func_module; /* The __module__ attribute, can be anything */
    PyObject *func_annotations; /* Annotations, a dict or NULL */
    PyObject *func_qualname; /* The qualified name */

    /* Invariant:
    * func_closure contains the bindings for func_code->co_freevars, so
    * PyTuple_Size(func_closure) == PyCode_GetNumFree(func_code)
    * (func_closure may be NULL if PyCode_GetNumFree(func_code) == 0).
    */
    } PyFunctionObject;

    我们可以看到有代码对象,然后
  • 全局变量字典,
  • 默认参数值,
  • 仅关键字参数默认值,
  • 函数的闭包单元,
  • 文档字符串,
  • 名称,
  • __dict__ ,
  • 对函数的弱引用列表,
  • __module__ ,
  • 注释,以及
  • __qualname__ ,完全限定名称

  • PyObject_HEAD宏,还有类型指针和一些引用计数/GC 元数据。

    我们不必直接使用 C 来检查大部分内容 - 我们可以查看 dir并过滤掉非实例属性,因为大部分数据在 Python 级别可用 - 但结构定义提供了一个漂亮的、注释的、整洁的列表。

    您可以查看 code object struct definition也是,但是如果您还不熟悉代码对象,内容就不是很清楚,所以我不打算将其嵌入帖子中。我将只解释代码对象。

    代码对象的核心组件是 Python 字节码指令和参数的字节串。我们之前检查了其中之一。此外,代码对象包含诸如函数引用的常量元组之类的东西,以及确定如何实际执行每条指令所需的许多其他内部元数据。并非所有元数据 - 其中一些来自函数对象 - 但很多。其中一些,如常量元组,相当容易理解,而另一些,如 co_flags (一堆内部标志)或 co_stacksize (用于临时值的堆栈大小)更深奥。

    关于python - 函数作为 Python 中的对象 : what exactly is stored in memory?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45746494/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com