Python,惰性列表

7 投票
4 回答
13943 浏览
提问于 2025-04-15 20:08

在Python中,能不能让一个列表按需计算呢?

举个例子:

a = 1
list = [a]
print list
#[1]
a = 2
print list
#[1]

如果这个列表是按需计算的,那么最后一行的结果会是[2]

4 个回答

7

Python其实并没有那么“懒惰”。

你可以用生成器来模拟一些懒加载的数据结构,比如无限列表等等。但如果你想用普通的列表语法,那就不能享受到这种懒加载的特性了。

11

我在寻找一个真正的懒加载列表实现时,偶然发现了这个帖子,觉得这个想法挺有趣,想试着搞明白。

下面的实现基本上就是最开始提问时想要的效果:

from collections import Sequence

class LazyClosureSequence(Sequence):
    def __init__(self, get_items):
        self._get_items = get_items

    def __getitem__(self, i):
        return self._get_items()[i]

    def __len__(self):
        return len(self._get_items())

    def __repr__(self):
        return repr(self._get_items())

你可以这样使用它:

>>> a = 1
>>> l = LazyClosureSequence(lambda: [a])
>>> print l
[1]
>>> a = 2
>>> print l
[2]

这显然是个糟糕的实现。

11

“懒惰”评估这个概念通常出现在函数式编程语言中,但在这些语言里,你不能把两个不同的值重新赋给同一个标识符,所以你的例子在那儿也无法实现。

关键并不是懒惰,而是使用一个标识符时,它总是指向那个标识符所引用的同一个值。而且,如果你把一个标识符,也就是一个“裸名”,重新赋值给一个不同的值,那么从那时起,这个标识符就会指向一个不同的值。对第一个值(对象)的引用并不会消失。

想象一个类似的例子,这里不涉及重新赋值给裸名,而是涉及其他类型的变更(当然是针对可变对象——数字和字符串是不可变的),包括赋值给其他东西,而不是裸名:

>>> a = [1]
>>> list = [a]
>>> print list
[[1]]
>>> a[:] = [2]
>>> print list
[[2]]

因为这里没有 a - ... 这样的操作去重新赋值裸名 a,而是 a[:] = ... 这样的操作去重新赋值 a内容,所以让Python变得“懒惰”是非常简单的(实际上,要让它变得“急切”还需要一些努力!)……如果懒惰和急切与这两种情况有任何关系的话(其实并没有;-)。

只需了解“赋值给裸名”的语义是非常简单的(与赋值给其他东西相比,后者可以通过适当使用你自己的类型来进行各种调整和控制),那么“懒惰与急切”的错觉就可能会消失了;-)

撰写回答