我注意到在列表理解中使用递归时会发生一些奇怪的事情。如果递归太深,解释器似乎会变得空闲(我等了5分钟,什么也没发生)。
出于这个问题的考虑,假设我想展平嵌套列表(我不想-但这是一个简短的代码示例,它说明了我所遇到的问题):
def flatten(x):
if isinstance(x, list):
return [a for i in x for a in flatten(i)]
else:
return [x]
使用帮助函数创建嵌套列表:
def wrap_in_lists(value, depth):
a = value
for _ in range(depth):
a = [a]
return a
使用时效果很好:
>>> flatten(wrap_in_lists(1, 2**10))
[1]
但是当我使用它时,它完全停止了:
>>> flatten(wrap_in_lists(1, 2**11))
# Nothing happens, no exception, no result, no segfault, ...
奇怪的是,使用生成器的类似方法没有显示此行为:
def flatten(l):
def inner(x):
for item in x:
if isinstance(item, list):
yield from inner(item)
else:
yield item
return list(inner(l))
>>> flatten(wrap_in_lists(1, 2**11))
[1]
>>> # although increasing the depth leads to an recursion error
>>> flatten(wrap_in_lists(1, 2**12))
RecursionError: maximum recursion depth exceeded
如果重要的话,我会在Jupyter实验室的Windows上使用Python 64位3.6.6。
这是一个简单的StackOverflow,发生在达到递归限制之前。
在第二种方法(生成器)中,深度达到的递归极限2**12
。这意味着2**11
第一种方法应该达到递归限制。这是因为列表综合会创建一个额外的堆栈框架,因此它是生成器解决方案的两倍。它不会引发RecursionError的事实意味着解释器发生了某些“致命”错误(或者某个地方存在无限循环)。
但是,这不是无限循环的,因为如果您检查jupyter实验室的响应(例如,如果从命令行启动jupyter lab
),您会注意到在运行该flatten(wrap_in_lists(1, 2**11))
行后不久它将打印一个kernel <xyz> restarted
。因此,没有响应是不正确的,内核只是崩溃了,[*]
在这种情况下在jupyter实验室单元中显示的只是意味着计算没有完成(由于崩溃)。
这就是为什么如果更改Python递归限制或使用为您更改了它的解释器时要非常小心的原因之一。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句