[英]Why does python memory allocation behave like this?
考虑以下代码,该代码用于查找所有带有 n 个括号的有效括号位置。
def paren(n):
ans = []
def helper(string, left, right,n,foo):
if len(string)==2*n:
ans.append(string)
if left > 0:
helper(string+'(', left-1,right,n)
if right > left:
helper(string+')', left,right-1,n)
helper('',n,n,n)
如果我们添加一个列表(在函数中没有实际用途),我们得到
def paren(n):
ans = []
def helper(string, left, right,n,foo):
print(hex(id(foo)), foo)
if len(string)==2*n:
ans.append(string)
if left > 0:
helper(string+'(', left-1,right,n,[])
if right > left:
helper(string+')', left,right-1,n,[])
helper('',n,n,n,[])
paren(2)
OUTPUT:
0x2e5e2446288 []
0x2e5e28e3508 []
0x2e5e28e3688 []
0x2e5e26036c8 []
0x2e5e27bafc8 []
0x2e5e28e3688 []
0x2e5e26036c8 []
0x2e5e27bafc8 []
而如果我们每次都明确地传递 foo 那么我们得到
def paren(n):
ans = []
def helper(string, left, right,n,foo):
print(hex(id(foo)), foo)
if len(string)==2*n:
ans.append(string)
if left > 0:
helper(string+'(', left-1,right,n,foo)
if right > left:
helper(string+')', left,right-1,n,foo)
helper('',n,n,n,[])
paren(2)
OUTPUT:
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
0x1c2cfec6288 []
在第一种情况下,我们在 memory 中得到不同的 object,为什么与第二种情况相比,我认为这与我们创建一个新列表而不是传递 ZC1C425268E687385D1AB14A507C 参数有关?
然而,当我们向 foo 添加一些东西时,我们会得到与第一种情况相同的行为:
def paren(n):
ans = []
def helper(string, left, right,n,foo):
print(hex(id(foo)), foo)
if len(string)==2*n:
ans.append(string)
if left > 0:
helper(string+'(', left-1,right,n,foo+['bar'])
if right > left:
helper(string+')', left,right-1,n,foo+['bar'])
helper('',n,n,n,[])
paren(2)
OUTPUT:
0x269572e6288 []
0x26959283548 ['bar']
0x26957363688 ['bar', 'bar']
0x2695925ae88 ['bar', 'bar', 'bar']
0x26957363408 ['bar', 'bar', 'bar', 'bar']
0x2695925ae88 ['bar', 'bar']
0x26957363408 ['bar', 'bar', 'bar']
0x269592833c8 ['bar', 'bar', 'bar', 'bar']
但奇怪的是,如果我们为 foo 传递一些 int,我将取 5 进行演示,我们得到:
def paren(n):
ans = []
def helper(string, left, right,n,foo):
print(hex(id(foo)), foo)
if len(string)==2*n:
ans.append(string)
if left > 0:
helper(string+'(', left-1,right,n,5)
if right > left:
helper(string+')', left,right-1,n,5)
helper('',n,n,n,5)
paren(2)
OUTPUT:
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
0x7ffef47293c0 5
即 memory 中的同一点。
但是,如果我用更大的 int 替换上述代码中的 5,例如 2550,我会得到以下结果:
0x2519f6d4790 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
所以最初它存储在不同的 memory 地址,但每个后续调用都在同一个地址。 为什么这与 foo=5 的情况不同,这里发生了什么?
同样在 memory 地址在调用之间发生变化的示例中,我确实看到相同的 memory 地址在多个场合使用,例如:
...
0x2695925ae88 ['bar', 'bar', 'bar']
...
0x2695925ae88 ['bar', 'bar']
...
为什么会这样? 一旦旧变量不再在递归调用堆栈中,python 是否使用以前使用的 memory 地址来存储新变量?
我对这些行为真的很模糊,所以如果有人能帮助我,那就太好了!
我听说过诸如按引用传递和按值传递之类的事情,但我不太确定它们的含义以及它是否与此 python 示例有关。
谢谢你。
第一个代码:调用堆栈保留对每个foo
的引用,因此您在 memory 中有许多列表,每个列表都有一个唯一的 ID。
第二个代码:您将相同的列表(最初为空)传递给每个递归调用。
第三个代码:Cpython,作为特定于实现的优化,缓存小的int
常量以供重用
第四个代码 Cpython不会缓存大(即大于 256),因此每次出现 2550 都会创建一个新的int
object。
我将尝试用通用的解释向您解释这种行为。
Python 区分参考数据类型和原始数据类型。 引用数据类型将 memory 中的引用保存为存储值,而不是它们自身的值。 另一方面,原始(或值)数据类型保存值本身。
如果你将一个数组传递给一个方法,它每次都会创建这个数组的一个新实例。 因此,执行some_function([])
会生成一个新数组并将其传递给 function。 这意味着调用some_function([])
然后再次调用 some_function([] some_function([])
将在每次调用时创建一个数组。 但是,如果您创建一个像foo = []
这样的数组,那么 foo 将引用该数组并调用some_function(foo)
然后some_function(foo)
将在该数组上执行some_function
。
对于 float、int、boolean 之类的值不会发生这种情况,但对于对象和 arrays 之类的值会发生这种情况。
可以通过以下关键字进行后续研究:引用数据类型、值数据类型、内置类型。
我希望我的回答有助于理解所述问题。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.