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

Wai*_*ung 41 c python arrays list

我有一个很大的清单l.我想从元素4到6创建一个视图.我可以使用序列切片来完成.

>>> l=range(10)
>>> lv=l[3:6]
>>> lv
[3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

然而,lv是l片的副本.如果我更改基础列表,则lv不会反映更改.

>>> l[4] = -1
>>> lv
[3, 4, 5]
Run Code Online (Sandbox Code Playgroud)

反之亦然我希望修改lv也反映在l中.除此之外,列表大小不会改变.

我不期待建立一个大班来做这件事.我只是希望其他Python大师可能知道一些隐藏的语言技巧.理想情况下,我希望它能像C中的指针算术一样.

int lv[] = l + 3;
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 32

Python标准库中没有"list slice"类(也没有内置的).所以,你确实需要一个类,虽然它不需要很大 - 特别是如果你满足于"只读"和"紧凑"切片.例如:

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)]
Run Code Online (Sandbox Code Playgroud)

这有一些限制(不支持"切片"),但对于大多数用途可能没问题.

为了使这个序列R/W你需要添加__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
Run Code Online (Sandbox Code Playgroud)

  • 但是如果你做'alist.insert(0,某事)`那么切片会移动!这可能是也可能不是问题...... (3认同)

unu*_*tbu 29

也许只是使用一个numpy数组:

In [19]: import numpy as np

In [20]: l=np.arange(10)
Run Code Online (Sandbox Code Playgroud)

基本切片numpy数组返回一个视图,而不是一个副本:

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

In [22]: lv
Out[22]: array([3, 4, 5])
Run Code Online (Sandbox Code Playgroud)

改变l影响lv:

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

In [24]: lv
Out[24]: array([ 3, -1,  5])
Run Code Online (Sandbox Code Playgroud)

改变lv影响l:

In [25]: lv[1]=4

In [26]: l
Out[26]: array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
Run Code Online (Sandbox Code Playgroud)


pyl*_*ang 7

对进行子类化,more_itertools.SequenceView通过改变序列来影响视图,反之亦然。

代码

import more_itertools as mit


class SequenceView(mit.SequenceView):
    """Overload assignments in views."""
    def __setitem__(self, index, item):
        self._target[index] = item
Run Code Online (Sandbox Code Playgroud)

演示

>>> 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]
Run Code Online (Sandbox Code Playgroud)

more_itertools是一个第三方库。通过安装> pip install more_itertools

  • 再加上一个让我发现“more_itertools”,尽管我不会使用你的代码 (3认同)

vir*_*tor 5

您可以使用原始列表引用创建自己的生成器.

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
Run Code Online (Sandbox Code Playgroud)

然而,这是一个生成器,你只能通过列表一次,前进,如果你删除的元素超过你的请求,它将爆炸range.


Mat*_*OFF 5

https://gist.github.com/mathieucaroff/0cf094325fb5294fb54c6a577f05a2c1

上面的链接是一个基于python 3范围能力的解决方案,可以在恒定时间内进行切片和索引。

它支持切片、相等比较、字符串转换 ( __str__) 和复制器 ( __repr__),但不支持赋值。

当检测到这种情况时,创建 SliceableSequenceView 的 SliceableSequenceView 不会减慢访问时间。

序列视图.py

# stackoverflow.com/q/3485475/can-i-create-a-view-on-a-python-list

try:
    from collections.abc import Sequence
except ImportError:
    from collections import Sequence # pylint: disable=no-name-in-module

class SliceableSequenceView(Sequence):
    """
    A read-only sequence which allows slicing without copying the viewed list.
    Supports negative indexes.

    Usage:
        li = list(range(100))
        s = SliceableSequenceView(li)
        u = SliceableSequenceView(li, slice(1,7,2))
        v = s[1:7:2]
        w = s[-99:-93:2]
        li[1] += 10
        assert li[1:7:2] == list(u) == list(v) == list(w)
    """
    __slots__ = "seq range".split()
    def __init__(self, seq, sliced=None):
        """
        Accept any sequence (such as lists, strings or ranges).
        """
        if sliced is None:
            sliced = slice(len(seq))
        ls = looksSliceable = True
        ls = ls and hasattr(seq, "seq") and isinstance(seq.seq, Sequence)
        ls = ls and hasattr(seq, "range") and isinstance(seq.range, range)
        looksSliceable = ls
        if looksSliceable:
            self.seq = seq.seq
            self.range = seq.range[sliced]
        else:
            self.seq = seq
            self.range = range(len(seq))[sliced]

    def __len__(self):
        return len(self.range)

    def __getitem__(self, i):
        if isinstance(i, slice):
            return SliceableSequenceView(self.seq, i)
        return self.seq[self.range[i]]

    def __str__(self):
        r = self.range
        s = slice(r.start, r.stop, r.step)
        return str(self.seq[s])

    def __repr__(self):
        r = self.range
        s = slice(r.start, r.stop, r.step)
        return "SliceableSequenceView({!r})".format(self.seq[s])

    def equal(self, otherSequence):
        if self is otherSequence:
            return True
        if len(self) != len(otherSequence):
            return False
        for v, w in zip(self, otherSequence):
            if v != w:
                return False
        return True
Run Code Online (Sandbox Code Playgroud)