Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/356.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中多个重复lambda函数的内存开销是多少?_Python - Fatal编程技术网

Python中多个重复lambda函数的内存开销是多少?

Python中多个重复lambda函数的内存开销是多少?,python,Python,我想知道Python解释器实际上是如何处理内存中的lambda函数的 如果我有以下资料: def squares(): return [(lambda x: x**2)(num) for num in range(1000)] 这是否会在内存中创建1000个lambda函数实例,或者Python是否足够聪明,知道这1000个lambda中的每一个都是相同的,从而将它们作为一个函数存储在内存中;DR:在您的示例中,lambda对象的内存开销是一个lambda的大小,但仅当squares(

我想知道Python解释器实际上是如何处理内存中的lambda函数的

如果我有以下资料:

def squares():
    return [(lambda x: x**2)(num) for num in range(1000)]

这是否会在内存中创建1000个lambda函数实例,或者Python是否足够聪明,知道这1000个lambda中的每一个都是相同的,从而将它们作为一个函数存储在内存中;DR:在您的示例中,lambda对象的内存开销是一个lambda的大小,但仅当
squares()
函数运行时,即使您持有对其返回值的引用,因为返回的列表不包含lambda对象

但是,即使您保留了从同一lambda表达式(或def语句)创建的多个函数实例,它们也共享同一个代码对象,因此每个额外实例的内存成本都小于第一个实例的成本


以你为例,

[(lambda x: x**2)(num) for num in range(1000)]
您只在列表中存储lambda调用的结果,而不是lambda本身,因此lambda对象的内存将被释放

lambda对象何时被垃圾回收取决于您的Python实现。CPython应该能够立即执行此操作,因为每个循环的引用计数都会降至0:

>>> class PrintsOnDel:
...     def __del__(self):
...       print('del')  # We can see when this gets collected.
...
>>> [[PrintsOnDel(), print(x)][-1] for x in [1, 2, 3]]  # Freed each loop.
1
del
2
del
3
del
[None, None, None]
PyPy是另一个故事

>>>> from __future__ import print_function
>>>> class PrintsOnDel:
....   def __del__(self):
....     print('del')
....
>>>> [[PrintsOnDel(), print(x)][-1] for x in [1, 2, 3]]
1
2
3
[None, None, None]
>>>> import gc
>>>> gc.collect()  # Not freed until the gc actually runs!
del
del
del
0
随着时间的推移,它将创建1000个不同的lambda实例,但它们不会同时都在内存中(在CPython中),而且它们都指向同一个代码对象,因此一个函数有多个实例并不像听起来那么糟糕:

>>> a, b = [lambda x: x**2 for x in [1, 2]]
>>> a is b  # Different lambda objects...
False
>>> a.__code__ is b.__code__  # ...point to the same code object.
True

分解字节码可以帮助您准确理解解释器在做什么:

>>> from dis import dis
>>> dis("[(lambda x: x**2)(num) for num in range(1000)]")
  1           0 LOAD_CONST               0 (<code object <listcomp> at 0x000001D11D066870, file "<dis>", line 1>)
              2 LOAD_CONST               1 ('<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_NAME                0 (range)
              8 LOAD_CONST               2 (1000)
             10 CALL_FUNCTION            1
             12 GET_ITER
             14 CALL_FUNCTION            1
             16 RETURN_VALUE

Disassembly of <code object <listcomp> at 0x000001D11D066870, file "<dis>", line 1>:
  1           0 BUILD_LIST               0
              2 LOAD_FAST                0 (.0)
        >>    4 FOR_ITER                16 (to 22)
              6 STORE_FAST               1 (num)
              8 LOAD_CONST               0 (<code object <lambda> at 0x000001D11D0667C0, file "<dis>", line 1>)
             10 LOAD_CONST               1 ('<listcomp>.<lambda>')
             12 MAKE_FUNCTION            0
             14 LOAD_FAST                1 (num)
             16 CALL_FUNCTION            1
             18 LIST_APPEND              2
             20 JUMP_ABSOLUTE            4
        >>   22 RETURN_VALUE

Disassembly of <code object <lambda> at 0x000001D11D0667C0, file "<dis>", line 1>:
  1           0 LOAD_FAST                0 (x)
              2 LOAD_CONST               1 (2)
              4 BINARY_POWER
              6 RETURN_VALUE

因此,它会重新使用代码对象。

您的代码创建一个lambda函数并调用它1000次,而不会在每次迭代时创建一个新对象。因为在每次迭代中,存储的是lambda函数的结果,而不是函数本身。这相当于:

def square(x): return x*x # or def square x = lambda x: x*x
[square(x) for x in range(1000)]
通过这种方式,我们将为每次迭代创建一个lambda函数对象。请参见此虚拟示例:

[lambda x: x*x for _ in range(3)]
给出:

[<function <listcomp>.<lambda> at 0x294358950>, 
 <function <listcomp>.<lambda> at 0x294358c80>, 
 <function <listcomp>.<lambda> at 0x294358378>]
[,,
, 
]

lambda的内存地址都不同。然后为每个lambda创建一个不同的对象。

所有这些函数对象都会立即消失。示例函数返回一个列表,而不是生成器。这似乎是一个没有人会写的人为的例子。这就是你想问的吗?没有人会从函数返回一个列表?@AlexWallish:Noone会故意在一个列表中返回完全相同的结果1000次(这迫使内容完全扩展;不像我说的生成器)。(他们为什么要这么做?)告诉我们更多关于上下文的信息。我试图理解这是否是一个真正的问题。这显然是一个例子。正如@gilch所说,重点显然不是制作一个方块列表,而是询问python如何处理内存中的lambda函数。
[<function <listcomp>.<lambda> at 0x294358950>, 
 <function <listcomp>.<lambda> at 0x294358c80>, 
 <function <listcomp>.<lambda> at 0x294358378>]