我可以在Python列表上创建“视图”吗?
我有一个很大的列表 l
。我想从第4个元素到第6个元素创建一个视图。我可以通过切片来实现这个。
>>> l = range(10)
>>> lv = l[3:6]
>>> lv
[3, 4, 5]
但是 lv
是 l
列表切片的一个副本。如果我改变了原始列表,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