Python 地图的惰性评价
我最近读到Python3中的Python 地图的惰性评价,python,lazy-evaluation,map-function,Python,Lazy Evaluation,Map Function,我最近读到Python3中的map的一个好处是它很懒惰。也就是说,这样做更好 map(lambda x: x**2, range(10**100)) 而不是 [x**2 for x in range(10**100)] 我好奇的是,我如何利用这种懒惰。例如,如果生成映射对象,如何访问结果操作/列表中的特定元素。在我所看到的几乎所有关于map的文档中,他们都会为map中的I(…)或执行类似于print(map(…)的操作,据我所知,这会放弃惰性概念,因为它会隐式地将map转换为列表 我想我正在
map
的一个好处是它很懒惰。也就是说,这样做更好
map(lambda x: x**2, range(10**100))
而不是
[x**2 for x in range(10**100)]
我好奇的是,我如何利用这种懒惰。例如,如果生成映射对象,如何访问结果操作/列表中的特定元素。在我所看到的几乎所有关于map
的文档中,他们都会为map中的I(…)或执行类似于print(map(…)
的操作,据我所知,这会放弃惰性概念,因为它会隐式地将map转换为列表
我想我正在寻找的是以类似于range
的惰性方式使用映射对象的能力,在这里我可以做x=range(10**100)
并在没有巨大计算负载的情况下惰性地生成x[10000]
如果这个概念不存在,那么让map
懒惰有什么好处?如果您总是需要将其转换为一些非惰性对象(如列表),那么map
是惰性的又有什么关系呢?首先,请注意range
(xrange
在Python 2中)是一个特例。它不是一个简单的生成器,也不只是返回一个列表。它还支持
操作中的,这不是iterables或迭代器的标准功能
假设map(func,iterable)
可以在无限iterable上调用,或者在iterable上获取下一个值的过程非常耗时
您需要知道您的函数可能会处理这些类型的值,并确保使用惰性函数,如itertools.imap
。既然基本上不可能确定迭代器是无限的,即使在运行时也是如此,那么内置函数是否应该在最广泛的输入范围内正确运行呢
并非每个用例都需要随机访问,那些需要随机访问的用例必须完全实例化iterable或使用另一个itertools
函数,如islice
,有很多好处;例如,它使编写内存效率高的代码变得更容易
def take_up_a_lot_of_memory(*args):
"""
A contrived example of a function that uses a lot of memory
"""
return sum([i ** 2 for i in range(10 ** 6)])
megasum = sum(map(take_up_a_lot_of_memory, range(1000)))
此外,有时您可以提前终止计算,而无需遍历所有映射结果,从而避免冗余。您在这里比较的是苹果和桔子<代码>范围
不是只是一个懒惰的变量。它是一个特定的对象,其内容满足特定的规则,允许支持许多操作,而无需在内存中实际构建庞大的序列。这是因为范围
的第n个元素基本上就是开始+n*步
(模停止
,符号等)
然而,map
意味着可以使用任何函数f
。特别是,函数可能具有共享/全局状态,这已经使得不执行100个函数调用就无法执行map(f,something)[100]
。不这样做会破坏结果的正确性
map
是惰性的,这意味着它不会立即构建一个完整的结果列表,而是在调用f
并生成它之前,等待您需要下一个结果。这样可以避免在代码中生成不必要的列表,如:
for x in map(f, iterable):
# do something with x
如果map
是渴望的,则执行循环将消耗iterable
的两倍内存,而使用惰性map
时,所需的唯一空间基本上是x
的空间
此外,它还允许对无限多个iterable(如count()
)调用map
。这显然会导致一个永无止境的程序做一些事情,或者在某个时候,您可以停止查看map
。急切的map
无法处理此情况
如果您想使用自己的受限映射,该映射只适用于纯函数,并且允许随机访问,那么您可以编写自己的类:
class PureMap:
def __init__(self, function, sequence):
self._f = function
self._sequence = sequence
def __iter__(self):
return map(self._f, self._sequence)
def __getitem__(self, i):
return self._f(self._sequence[i])
# etc.
但是,即使在这种情况下,您也会遇到一些问题:
sequence
实际上是一个iterable
要获得第n个元素,您必须使用前n个元素。之后,您必须将它们作为序列存储在类中,以备将来使用。但是这已经破坏了整个事情的目的,因为执行PureMap(f,sequence)[1000]
无论如何都需要在内存中存储1000
元素,即使它避免了999
调用f
f
。这意味着您还必须跟踪哪个元素已经计算过,哪个元素没有计算过- 被调用的函数是纯函数
- iterable参数类似于
,它允许随机访问,而无需生成其他元素range
- 您调用的函数速度很快,因此您可以在各种元素上重新计算它,而不用太担心性能
当所有这些假设都满足时,您可以拥有一个“工作方式类似于
range
”的map对象。如果您需要第10000个元素,您可能需要计算前面的99999(除非您有一个简单的函数,如x**2
,在这种情况下只需执行10000**2
),您无法“索引到”它。懒惰是一个优势,因为你不需要一次在内存中保存整个列表。那么我的观点是,懒惰映射如何有用(而不是多余)?如果您关心内存,只需使用生成器。在您的示例中,迭代映射
并不比迭代等效的生成器表达式好多少。两者都是懒散地评估的。与列表理解相比,它的优势在于不存储整个列表(这可能是不必要的,甚至是不可能的),这只是一个优势,如果您不需要同时存储所有列表。不清楚您想问什么:map
vs列表<代码>地图<