如何为 Python 列表/集合设置最大长度?

20

在 C/C++ 中,我们可以有:

maxnum = 10;
double xlist[maxnum];

如何为Python列表/集合设置最大长度?


这样我就可以将我的贪婪搜索限制在前x个结果上。否则,不断添加、排序和删除超过x个元素的操作有点浪费。 - alvas
2
一种方法是创建自定义列表类,并继承Python list的功能。然后在add(和可能的其他)方法中添加最大长度检查。 - stalk
@stalk 你应该把那个发表为一个答案。 - Ashwini Chaudhary
5个回答

26

你不需要这样做。

Python列表会根据需要动态增长和缩小以适应其内容。集合被实现为哈希表,就像Python字典一样,会根据需要动态增长和缩小以适应其内容。

也许你正在寻找 collections.deque(它使用 maxlen 参数),或者使用 heapq 的一些东西(在达到最大值时使用 heapq.heappushpop())?


我认为你关于字典收缩的看法是错误的,或者说至少有些误导性。当我创建一个空字典时,sys.getsizeof 告诉我它占用了148个字节。在添加一百万个条目之后,它占用了25165876个字节。在弹出所有条目后,它仍然是25165876个字节。此外,如果我尝试 next(iter(d)),在添加了一百万个条目后,速度比仅剩一个条目时快了约3500倍(这实际上是我注意到这一点的原因)。 - Stefan Pochmann
@StefanPochmann 改变大小的操作会被推迟,直到您再次添加元素(如我记得的那样)。我需要检查确切的触发条件。我知道最常见的用例是删除后紧接着添加新元素,这也是针对其进行了优化的原因,因此在删除时没有立即缩小容器。 - Martijn Pieters
@StefanPochmann 不在笔记本电脑旁边,但是 Tim Peters 发送的这封电子邮件解释了插入如何触发调整大小:https://mail.python.org/pipermail/python-dev/1999-August/000667.html。 - Martijn Pieters
@MartijnPieters 谢谢,不过可能已经不正确了。当我再次添加项目时,我的字典保持在25,165,876字节,直到最终跳到50,331,700字节。这个评论在当前代码中确实说“新表可能比旧表小”,但我还没有能够真正实现这一点。 - Stefan Pochmann
@StefanPochmann:它仍然是当前的,但我还没有研究所有触发调整大小的方法。搜索实际调用dictresize的位置。 - Martijn Pieters
@ColonelPanic,使用deque可以给你一个有限大小和插入时过期的队列,但你会失去索引访问。我没有看到任何原生队列支持索引访问或列表支持在最大大小时过期。 - stoooops

11

这里是Python的list的扩展版本。它的行为类似于list,但如果长度超出限制,会引发BoundExceedError错误(在Python 2.7中尝试):

class BoundExceedError(Exception):
    pass


class BoundList(list):
    def __init__(self, *args, **kwargs):
        self.length = kwargs.pop('length', None)
        super(BoundList, self).__init__(*args, **kwargs)

    def _check_item_bound(self):
        if self.length and len(self) >= self.length:
            raise BoundExceedError()

    def _check_list_bound(self, L):
        if self.length and len(self) + len(L) > self.length:
            raise BoundExceedError()

    def append(self, x):
        self._check_item_bound()
        return super(BoundList, self).append(x)

    def extend(self, L):
        self._check_list_bound(L)
        return super(BoundList, self).extend(L)

    def insert(self, i, x):
        self._check_item_bound()
        return super(BoundList, self).insert(i, x)

    def __add__(self, L):
        self._check_list_bound(L)
        return super(BoundList, self).__add__(L)

    def __iadd__(self, L):
        self._check_list_bound(L)
        return super(BoundList, self).__iadd__(L)

    def __setslice__(self, *args, **kwargs):
        if len(args) > 2 and self.length:
            left, right, L = args[0], args[1], args[2]
            if right > self.length:
                if left + len(L) > self.length:
                    raise BoundExceedError()
            else:
                len_del = (right - left)
                len_add = len(L)
                if len(self) - len_del + len_add > self.length:
                    raise BoundExceedError()
        return super(BoundList, self).__setslice__(*args, **kwargs)

使用方法:

>>> l = BoundList(length=10)
>>> l.extend([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
>>> l
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> # now all these attempts will raise BoundExceedError:
>>> l.append(11)
>>> l.insert(0, 11)
>>> l.extend([11])
>>> l += [11]
>>> l + [11]
>>> l[len(l):] = [11]

我需要导入任何库或其他东西吗? - alvas

10

一旦你拥有了列表lst,你就可以

if len(lst)>10:
    lst = lst[:10]

如果大小超过10个元素,则将其截断为前10个元素。


2
正如 @JonasR 指出的那样,在截断之前检查 len(lst) 是多余的。 - alvas
尝试运行这段代码 x=[1,2,6]; x = x[:2] if len(x)>2 else x,然后再尝试运行这段代码 x=[1,2,6]; x[:2] - alvas

3
你不能,列表和集合具有动态性质,可以扩展到任何大小。
Python不是C++,Python是一种动态语言。集合和列表可以扩展或缩小到任何大小。
如果您想从可迭代对象中获取x个最小或最大项,请使用heapq模块。
heapq.nsmallest(n, iterable[, key])

从可迭代对象中定义的数据集返回包含 n 个最小元素的列表。如果提供了 key,则指定一个带有一个参数的函数,用于从可迭代对象中的每个元素提取比较键:key=str.lower 等效于:sorted(iterable, key=key)[:n]

或者也可以使用 bisect 模块:

该模块提供了维护已排序列表的支持,而无需在每次插入后对列表进行排序。

然后使用切片或 itertools.slice 从列表中获取前 x 个项目。


0

你可以使用先分配内存的解决方案

[0] * maxnum

或者

[a sample of your object] * maxnum

请注意,此解决方案不会像C++语言一样在追加超过列表最大大小时抛出错误。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接