我可以创建一个;“视图”;在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)