python - 为什么python内存分配会这样?
问题描述
考虑以下代码,该代码用于查找所有带有 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 []
在第一种情况下,我们在内存中获得了一个不同的对象,为什么与第二种情况相比,我认为这与我们创建一个新列表而不是传递函数参数有关?
然而,当我们向 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
即内存中的同一点。
但是,如果我将上述代码中的 5 替换为更大的 int,例如 2550,我会得到以下结果:
0x2519f6d4790 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
0x2519f9ec6f0 2550
所以最初它存储在不同的内存地址,但每个后续调用都在相同的地址。为什么这与 foo=5 的情况不同,这里发生了什么?
同样在内存地址在调用之间发生变化的示例中,我确实看到不止一次使用相同的内存地址,例如:
...
0x2695925ae88 ['bar', 'bar', 'bar']
...
0x2695925ae88 ['bar', 'bar']
...
为什么会这样?一旦旧变量不再在递归调用堆栈中,python 是否使用以前使用的内存地址来存储新变量?
我对这些行为真的很模糊,所以如果有人能帮助我,那就太好了!
我听说过诸如按引用传递和按值传递之类的东西,但我不太确定它们的含义以及它是否与这个 python 示例有关。
谢谢你。
解决方案
第一个代码:调用堆栈保留对 each 的引用foo
,因此您在内存中一次有许多列表,每个列表都有一个唯一的 ID。
第二个代码:您将相同的列表(最初为空)传递给每个递归调用。
第三个代码:Cpython,作为特定于实现的优化,缓存小int
常量以供重用
第四个代码 Cpython 不会缓存大(即大于 256),所以每次出现 2550 都会创建一个新int
对象。
推荐阅读
- javascript - CSS/JS扩展三级下拉导航,第三个子菜单项不可点击,只关闭父LI
- python - AttributeError:“ElasticLoadBalancingv2”对象没有属性“set_desired_capacity”
- javascript - 在 React Native 中导入图形工具包导致错误 500
- javascript - 如果 else 语句不起作用,则掷骰子游戏
- c# - 通过 ajax 将视图模型发送到控制器 - 它是如何工作的?
- node.js - 使用 AAD v1.0 时如何获取多个资源 URL 的访问令牌
- mongodb - 在 MongoDB 中检索项目时在 Flask 中查看错误
- javascript - JavaScript:创建元素后不能立即选择元素?
- android-studio - 如何在嵌套 caldroid 的片段内修复“空对象引用上的布尔 androidx.fragment.app.FragmentManagerImpl.isDestroyed()”
- powershell - 如何检查对象数组中是否存在行?