[英]How does python "nonlocal" keyword implement? Can it save space?
现在我正在考虑编写一个代码来获得二叉树的最大深度。 Python 不能通过引用传递非容器参数,所以一般有两种选择,使用nonlocal
关键字或通过复制传递深度。
第一个get_max_depth1
需要更多的回溯操作,我想知道它是否比get_max_depth1
花费更少的空间。 如果 python 实现了nonlocal
使用参数通过引用传递,那么每个 function 还带一个 integer 指针,在这种情况下,它不亚于get_max_depth2
运行速度更慢,并且节省空间。 如果没有的话,二叉树深度为100000时, get_max_depth1
只需要一个变量, get_max_depth2
需要100000个变量d
保存在他们的function中,我猜想外面写d
是有意义的。
def main():
root = BinaryTreeNode()
d = 0
maxd1 = 0
def get_max_depth1(node):
nonlocal d,maxd1
maxd1 = max(maxd1, d)
if node.left:
d += 1
get_max_depth1(node.left)
d -= 1
if node.right:
d += 1
get_max_depth1(node.right)
d -= 1
get_max_depth1(root)
maxd2 = 0
def get_max_depth2(node, d):
nonlocal maxd2
maxd2 = max(maxd2, d)
if node.left:
get_max_depth2(node.left, d+1)
if node.right:
get_max_depth2(node.right, d+1)
get_max_depth2(root, 0)
如果您想确切了解分配差异是什么,那么使用https://pypi.org/project/memory-profiler/运行应用程序应该会给您想要的答案。 但这仅适用于非常理论的方面,结果将特定于一个 CPython 版本,可能无法保持整体。
在实践中,答案是:出于以下几个原因,它们大致相同:
10^30102
元素) Python 的数据 model将用户定义的函数定义为具有__closure__
属性,这具体化了function 闭包:封闭范围的可读/可写值。 __closure__
是None
或单元格的tuple
。
目前的标准(3.10)只定义了一个单元格有一个cell_contents
属性来表示单元格的值。 CellType
不保证它提供了什么。
值得注意的是,单元格是否可写并不取决于 function 是否将闭包捕获为可读(裸使用)或可读/可写( nonlocal
声明)。 两者都是同一种细胞。
在实践中,CPython¹ 将__closure__
表示为一个常规tuple
,每个单元格表示为一个 40 字节的 object ,其中包含对其值的引用。
>>> def outer(a = 3):
... def inner():
... print(a) # `a` is captured from outer scope
... return inner
>>> outer()
<function __main__.outer.<locals>.inner()>
>>> outer().__closure__
(<cell at 0x10eac2ca0: int object at 0x109f26d30>,)
>>> outer().__closure__[0].cell_contents
3
>>> # size of the closure tuple and cell in bytes
>>> sys.getsizeof(outer().__closure__), sys.getsizeof(outer().__closure__[0])
(48, 40)
__closure__
本身属于 function object,而一个cell
在所有关闭同一变量的函数之间共享。
相反,局部变量存储为引用数组——每个 8 字节。 本地存储属于 function调用,因此多次调用 function 也会创建多个此类引用。
作为参考,只是上面的inner
function object 的 shell 是 136 字节。 它的名称和完全限定名称分别为 54 和 69 字节。 它的字节码是 45 个字节。 您可能甚至不知道存在的东西有许多额外的费用。
在尝试保护 8 字节的单个块时请记住这一点。
¹CPython 3.8.12 [Clang 11.0.0 (clang-1100.0.33.17)],64 位构建。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.