我可以在Python列表上创建“视图”吗?

65 投票
11 回答
19869 浏览
提问于 2025-04-16 02:48

我有一个很大的列表 l。我想从第4个元素到第6个元素创建一个视图。我可以通过切片来实现这个。

>>> l = range(10)
>>> lv = l[3:6]
>>> lv
[3, 4, 5]

但是 lvl 列表切片的一个副本。如果我改变了原始列表,lv 就不会反映这个变化。

>>> l[4] = -1
>>> lv
[3, 4, 5]

反过来,我希望对 lv 的修改也能在 l 中体现出来。此外,列表的大小不会改变。

我不想为了这个创建一个复杂的类。我只是希望其他Python高手能知道一些隐藏的语言技巧。理想情况下,我希望它能像C语言中的指针运算那样:

int lv[] = l + 3;

11 个回答

9

你可以通过创建自己的生成器来实现这个功能,使用原始列表的引用。

l = [1,2,3,4,5]
lv = (l[i] for i in range(1,4))

lv.next()   # 2
l[2]=-1
lv.next()   # -1
lv.next()   # 4

不过,因为这是一个生成器,所以你只能一次性从头到尾遍历这个列表。如果你删除的元素超过了你用range请求的数量,程序就会出错。

32

也许可以直接使用一个numpy数组:

In [19]: import numpy as np

In [20]: l=np.arange(10)

基本的切片操作在numpy数组中返回的是一个视图,而不是一个副本:

In [21]: lv=l[3:6]

In [22]: lv
Out[22]: array([3, 4, 5])

修改l会影响到lv

In [23]: l[4]=-1

In [24]: lv
Out[24]: array([ 3, -1,  5])

而修改lv也会影响到l

In [25]: lv[1]=4

In [26]: l
Out[26]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
42

在Python的标准库里,没有“列表切片”这个类(也没有内置的)。所以,你确实需要一个类,但这个类可以很简单——特别是如果你只需要一个“只读”和“紧凑”的切片的话。例如:

import collections

class ROListSlice(collections.Sequence):

    def __init__(self, alist, start, alen):
        self.alist = alist
        self.start = start
        self.alen = alen

    def __len__(self):
        return self.alen

    def adj(self, i):
        if i<0: i += self.alen
        return i + self.start

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

这个方法有一些限制(比如不支持“切片的切片”),但对于大多数用途来说可能是可以的。

如果你想让这个序列可以读写,你需要添加 __setitem____delitem__insert 这几个功能:

class ListSlice(ROListSlice):

    def __setitem__(self, i, v):
        self.alist[self.adj(i)] = v

    def __delitem__(self, i, v):
        del self.alist[self.adj(i)]
        self.alen -= 1

    def insert(self, i, v):
        self.alist.insert(self.adj(i), v)
        self.alen += 1

撰写回答