此程序的Python内存模型
我有一个关于下面这个程序的问题,主要是关于存储值和函数的符号,当我在 http://pythontutor.com/ 上运行时的情况。
我的问题是:
在开始解释这个Python程序之前,Python的执行模型在内存中是怎样的?我该如何想象这个内存布局?比如说,C语言的可执行文件有代码区、栈区、堆区、额外数据段等等,这只是举个例子,并不是在比较。
‘const’是一个32位或64位的内存区域的名字吗?这个区域存储的值是2,并且类型被指定为整数?
根据图示,add()/sub()/其他函数在对象列中显示。那么,我该如何理解函数作为对象被存储?我该如何想象这一点?
根据图示,op是一个指向sub()函数的函数指针吗?
3 个回答
在Python中,不用太担心内存的细节和背后的运作。其实,环境(作用域)更为重要。你提到的块和指针的图示是一个很好的方式来理解内存。白色部分展示了全局环境的样子。当调用一个函数时,会创建一个新的(蓝色的)环境。
const
是一个变量。在Python中,变量是动态类型的,可以存储任何东西。实际上,Python中的整数不会溢出,可以存储超过264的数字。在这个例子中,const
是一个变量(名字有点让人困惑),它的值是数字2
。函数是一种可以调用的代码块的抽象概念。你可以像对待其他值一样把它赋值给一个变量。
如果你觉得这样更舒服,可以把它看作是一个函数指针,但那样的话你就暴露了自己是个C语言程序员。Python程序员会直接说
op
的值是函数sub
。
字典中的字典。字典是Python中最重要的结构,排第一。
这是当前作用域字典中一个条目的键。它的值是对象
2
。并不是说函数都是对象,而是有些对象是函数。或者是数字。或者是字典。
这是当前作用域字典中一个条目的键。它的值是
sub
。
每个C语言(编译型语言)程序在执行之前,都会在内存中加载代码、数据、栈、额外部分和堆等几个区域。那么,Python解释器在开始解释Python程序之前,会为每个Python程序创建内存布局吗?如果会的话,我该如何可视化这个内存布局呢?
Python确实有一种内存布局,但其中最重要的部分是堆,因为所有对象都会放在堆里。代码段只是解释器本身,数据段则是解释器的内部状态,栈也是如此。
对于Python程序来说,最相关的就是堆了。不过,这个布局和其他程序的布局是类似的。
const
是一个存储值2
的32/64位内存区域的名称吗?这个值的类型被指定为整数?
它其实是当前工作空间中的一个名字(这里指的是模块的命名空间),本质上是一个字典,用来在字符串和任意对象之间进行赋值。在这个例子中,它让字符串const
指向一个整数对象,这个对象的值是2
。(这个对象可以根据情况新建或重用;这没有区别,因为它是不可变的。)
add()
/sub()
等函数在图中显示在对象列中,那么,我该如何理解函数作为对象存储的呢?我该如何可视化它?
正如我在对Ignacio回答的评论中所写的:
对于函数来说,你有一个对象,它有一些字段,比如包含字节码的代码、参数数量等等。这个对象本身也有方法,比如__get__()
,它在内部用于将方法绑定到对象,或者__call__()
用于实际的函数调用,还有__format__()
、__repr__()
等。
一个整数对象内部有一个地方用来存储实际的值。在Python 2中是long()
,在Python 3中是int()
,它存储数据以保持值(比如2
)以及所需的长度。此外,它还有很多方法。你可以查看dir(2)
的输出,看到它有一堆方法,比如用于格式化、用于算术运算等等。
根据图示,
op
是指向函数sub()
的函数指针吗?
可以这么说,是的。
这里有一个函数对象,它内部知道它的原始名称是sub
。但这个信息只是用于显示。
在你的例子中,它是通过两个名字op
和sub
来引用的。因此,引用其中任何一个都会得到相同的结果。
需要注意的是,并没有所谓的“函数指针”,只有引用或名称,它们指向任何类型的对象。对象的类型是固定的,但“引用的类型”并不存在(因为没有这样的概念)。