提问人:matchaicedtea 提问时间:6/13/2023 更新时间:6/14/2023 访问量:59
使用 id() 在内存中传递对象引用 Python Pass-By-Object-Reference
Python Pass-By-Object-Reference in memory using id()
问:
我正在尝试参考 Robert Heaton 的文章测试一些代码,该文章解释了将参数传递到函数的不同概念之间的区别。
为什么 list 和 myList 存储在内存中的同一位置,而根据 Heaton 的说法,它们应该是两个完全独立的变量。
这是我写的:
def main():
myList = [0]
print(f"myList: {id(myList)}")
print(f"[0]: {id([0])}")
append(myList)
print(f"After call: {myList}")
def append(list):
print(f"append's list: {id(list)}")
list.append(1)
根据 Heaton 的说法,通过按对象引用传递,“函数提供了自己的框并为自己创建了一个新变量”。因此,我希望 id(myList) 和 id(list) 应该是存储在内存中不同位置的两个不同变量。
但是,我收到的输出是:
myList: 1941960670528
[0]: 1941963219200
append's list: 1941960670528
After call: [0, 1]
答:
你必须考虑整个段落。这篇文章并不是说它应该是 2 个不同的对象。有一个列表,但单独的变量指向它。
请考虑以下几点
(变量更改为,因为内置变量不应用于变量名称。list
l
def append(l):
print(f"append's list: {id(l)}")
l.append(1)
l = []
print(f"append's list after reassigning: {id(l)}")
myList = [0]
print(f"myList: {id(myList)}")
append(myList)
print(f"After call: {myList}")
print(f"myList id after call: {id(myList)}")
我们重新分配到一个新列表,但外部仍然指向包含l
myList
[0, 1]
它本质上试图展示的是,一个变量有 3 个部分——名称、引用及其值。名称 - 例如 , - 是我们在代码中引用的名称。它们又被解析为一个引用 - 简化,给出的值 - 在内存中找到一个存储实际值的位置。
因此,当我们将列表传递给函数时,相同的引用会被赋予一个我们可以在函数内部使用的新名称。更改与此引用相关的值将对外部函数可见,因为引用是相同的。如果我们做了一些改变引用的事情,例如用 分配一个新列表,这不会影响外部列表,因为它仍然使用旧的引用。l
myList
id()
l = []
总而言之,我们有 2 个关系:name -> reference 和 reference -> value
调用函数时,我们给引用一个新名称,结果如下:
name_outer -\
>- reference -- value
name_inner -/
更改该值会导致 像这样:
name_outer -\
>- reference -- new_value
name_inner -/
在更改引用结果时:
name_outer -- reference -- value
name_inner -- new_reference -- new_value
评论
string
int
id([])
id
id
据我所知(但也许我被教过不同的术语),当这篇文章说 Python 有一个特殊的函数调用模式时,它是错误的。Python 使用按值调用,这仅仅意味着(据我所知)函数调用将值的位复制到形式参数的值中(即在寄存器或堆栈中)。f(x)
x
f
使其行为如描述的那样是 Python 值实际上是指向包含实际对象的内存区域的指针。变量对应于一个内存位置(例如,它可以是“寄存器”,或者“地址指向的位置上方 7 个字节”),它指示指针的实际位置。rcx
rbp
请注意,此行为与其他隐藏指针和内存管理的语言(如 Java、OCaml 或 Smalltalk)相同。即使使用这种编译模式,值也并不总是指向存储实际数据的内存区域的指针:事实上,如果我们考虑与指针大小相同的不可变数据,人们可能会忍不住不为该数据在堆上的某个地方分配机器字。这可能会使 GC 逻辑变得相当复杂,但例如在 OCaml 中为整数实现。
关于你的例子中发生的事情的@matszwecja解释对我来说似乎是正确的,所以我不会在这里重复。
此外,作为旁注,Python 的规范并没有告诉它泄漏了变量值所指向的地址。只是当前的实现是这样做的,因为它满足规范,但这在 Jython 或 PyPy 中不起作用。id
评论