我可以创建一个;“视图”;在Python列表上?
我有一个很大的列表我可以创建一个;“视图”;在Python列表上?,python,arrays,list,slice,Python,Arrays,List,Slice,我有一个很大的列表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中。除此之外,列表大小不会改变 我并不期待建立一个大类来做这件事
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;
您可以通过使用原始列表引用创建自己的生成器来实现这一点
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
然而,作为一个生成器,您只能向前浏览列表一次,如果您删除的元素超过了使用范围请求的元素,则列表将爆炸。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)]
您可以编辑:不执行以下操作
shiftedlist = type('ShiftedList',
(list,),
{"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
)([1, 2, 3, 4, 5, 6])
本质上是一行,它不是很像蟒蛇,但这是基本要点
edit:我后来才意识到这是行不通的,因为list()
基本上会对传递的列表进行浅层复制。因此,这将或多或少地等同于对列表进行切片。实际上更少,因为缺少对\uuu len\uuu
的覆盖。您需要使用代理类;有关详细信息,请参阅。也许只需使用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])
编辑:对象参数必须是支持缓冲区调用接口(如字符串、数组和缓冲区)的对象。
-因此,很遗憾,不是这样
我想这就是你要找的
从链接页面粘贴示例:
>>> s = bytearray(1000000) # a million zeroed bytes
>>> t = buffer(s, 1) # slice cuts off the first byte
>>> s[1] = 5 # set the second element in s
>>> t[0] # which is now also the first element in t!
'\x05'
一旦您将从列表中获取一个片段,您将创建一个新列表。好的,它将包含相同的对象,因此只要涉及列表中的对象,它就将是相同的,但是如果修改切片,原始列表将保持不变
如果您真的想创建一个可修改的视图,您可以想象一个基于collection.MutableSequence
这可能是功能齐全的子列表的起点—它正确处理切片索引,但至少缺少负索引处理规范:
class Sublist(collections.MutableSequence):
def __init__(self, ls, beg, end):
self.ls = ls
self.beg = beg
self.end = end
def __getitem__(self, i):
self._valid(i)
return self.ls[self._newindex(i)]
def __delitem__(self, i):
self._valid(i)
del self.ls[self._newindex(i)]
def insert(self, i, x):
self._valid(i)
self.ls.insert(i+ self.beg, x)
def __len__(self):
return self.end - self.beg
def __setitem__(self, i, x):
self.ls[self._newindex(i)] = x
def _valid(self, i):
if isinstance(i, slice):
self._valid(i.start)
self._valid(i.stop)
elif isinstance(i, int):
if i<0 or i>=self.__len__():
raise IndexError()
else:
raise TypeError()
def _newindex(self, i):
if isinstance(i, slice):
return slice(self.beg + i.start, self.beg + i.stop, i.step)
else:
return i + self.beg
如果要按顺序访问“视图”,则可以使用itertools.islice(..)
您无法访问单个元素来更改切片中的元素,如果您更改了列表,则必须重新调用isclice(..)。子类通过改变序列来影响视图,反之亦然
代码
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
演示
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
是第三方图书馆。通过>pip安装更多\u itertools
上面的链接是一个基于Python3范围的解决方案,它可以被切片和删除
以恒定时间索引
它支持切片、相等比较、字符串转换(\u str\u
)和
复制器(\uuuu repr\uuuu
),但不支持分配
创建SliceableSequenceView的SliceableSequenceView不会减慢速度
检测到此情况时的访问时间
sequenceView.py
#stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list
尝试:
从collections.abc导入序列
除恐怖外:
从集合导入序列#pylint:disable=模块中没有名称
类SliceableSequenceView(序列):
"""
一种只读序列,允许在不复制已查看列表的情况下进行切片。
支持负索引。
用法:
li=列表(范围(100))
s=SliceableSequenceView(li)
u=SliceableSequenceView(li,切片(1,7,2))
v=s[1:7:2]
w=s[-99:-93:2]
li[1]+=10
断言li[1:7:2]==list(u)==list(v)==list(w)
"""
__插槽(seq range).split()
定义初始化(self,seq,sliced=None):
"""
接受任何序列(如列表、字符串或范围)。
"""
如果“切片”为“无”:
切片=切片(len(seq))
ls=looksSliceable=真
ls=ls和hasattr(seq,“seq”)和isinstance(seq.seq,Sequence)
ls=ls和hasattr(序列“范围”)和isinstance(序列范围,范围)
looksSliceable=ls
如果外观许可:
self.seq=seq.seq
self.range=序列范围[切片]
其他:
self.seq=seq
self.range=范围(len(seq))[切片]
定义(自我):
返回透镜(自量程)
定义获取项目(自我,i):
如果存在(i,切片):
返回SliceableSequenceView(self.seq,i)
返回self.seq[self.range[i]]
定义(自我):
r=自量程
s=切片(r.启动、r.停止、r.步进)
返回str(self.seq[s])
定义报告(自我):
r=自量程
s=切片(r.启动、r.停止、r.步进)
返回“SliceableSequenceView({!r})”.format(self.seq[s])
def相等(自身、其他顺序):
如果self是otherSequence:
返回真值
如果len(self)!=len(其他序列):
返回错误
对于zip中的v,w(self,otherSequence):
如果v!=w:
返回错误
返回真值
实际上,使用range
自己实现这一点并不太困难。您可以分割一个范围,它可以为您完成所有复杂的运算:
>>> range(20)[10:]
range(10, 20)
>>> range(10, 20)[::2]
range(10, 20, 2)
>>> range(10, 20, 2)[::-3]
range(18, 8, -6)
所以您只需要一个对象类,它包含对原始序列的引用和一个范围。下面是这样一个类的代码(我希望不是太大):
类序列视图:
import more_itertools as mit
class SequenceView(mit.SequenceView):
"""Overload assignments in views."""
def __setitem__(self, index, item):
self._target[index] = item
>>> seq = list(range(10))
>>> view = SequenceView(seq)
>>> view
SequenceView([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> # Mutate Sequence -> Affect View
>>> seq[6] = -1
>>> view[5:8]
[5, -1, 7]
>>> # Mutate View -> Affect Sequence
>>> view[5] = -2
>>> seq[5:8]
[-2, -1, 7]
>>> range(20)[10:]
range(10, 20)
>>> range(10, 20)[::2]
range(10, 20, 2)
>>> range(10, 20, 2)[::-3]
range(18, 8, -6)