55

私は大きなリストを持っています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;
4

10 に答える 10

37

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)]

これにはいくつかの制限があります(「スライスのスライス」はサポートされていません)が、ほとんどの目的で問題ない可能性があります。

このシーケンスを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
于 2010-08-14T23:11:25.427 に答える
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])
于 2010-08-14T23:25:44.757 に答える
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

ただし、これはジェネレーターであるため、リストを1回だけ確認して転送し、で要求したよりも多くの要素を削除すると爆発しますrange

于 2010-08-14T23:11:22.103 に答える
5

シーケンスを変更することでビューに影響を与えるようにサブクラス化し、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

デモ

>>> 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]

more_itertoolsサードパーティのライブラリです。経由でインストールし> pip install more_itertoolsます。

于 2018-02-14T03:21:39.410 に答える
5

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

上記のリンクは、一定時間でスライスおよびインデックス付けされるpython3範囲機能に基づくソリューションです。

スライス、等式比較、文字列キャスト(__str__)、および再現機能(__repr__)をサポートしますが、割り当てはサポートしません。

SliceableSequenceViewのSliceableSequenceViewを作成しても、このケースが検出されてもアクセス時間が遅くなることはありません。

sequenceView.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
于 2018-11-11T20:53:54.117 に答える
1

編集: The object argument must be an object that supports the buffer call interface (such as strings, arrays, and buffers). -残念ながら、いいえ。

バッファタイプはあなたが探しているものだと思います。

リンク先のページからの貼り付け例:

>>> 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' 
于 2010-08-15T00:32:44.467 に答える
1

リストからスライスを取得するとすぐに、新しいリストが作成されます。リストのオブジェクトが関係している限り、同じオブジェクトが含まれますが、スライスを変更しても元のリストは変更されません。

本当に変更可能なビューを作成したい場合は、に基づいて新しいクラスを想像することができます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

例:

>>> a = list(range(10))
>>> s = Sublist(a, 3, 8)
>>> s[2:4]
[5, 6]
>>> s[2] = 15
>>> a
[0, 1, 2, 3, 4, 15, 6, 7, 8, 9]
于 2015-12-18T17:16:11.167 に答える
0

あなたは編集することができます:次のようなことをしないでください

shiftedlist = type('ShiftedList',
                   (list,),
                   {"__getitem__": lambda self, i: list.__getitem__(self, i + 3)}
                  )([1, 2, 3, 4, 5, 6])

本質的にワンライナーであるため、それほどPythonicではありませんが、それが基本的な要点です。

編集:私は遅ればせながらlist()、渡されたリストの浅いコピーを本質的に行うため、これが機能しないことに気づきました。したがって、これはリストをスライスするのとほぼ同じになります。のオーバーライドが欠落しているため、実際には少なくなります__len__。プロキシクラスを使用する必要があります。詳細については、マルテッリ氏の回答を参照してください。

于 2010-08-14T23:16:30.640 に答える
0

を使用してこれを自分で実装することは実際にはそれほど難しくありませんrange。*範囲をスライスすることができ、複雑な計算をすべて実行します。

>>> range(20)[10:]
range(10, 20)
>>> range(10, 20)[::2]
range(10, 20, 2)
>>> range(10, 20, 2)[::-3]
range(18, 8, -6)

したがって、元のシーケンスへの参照と範囲を含むオブジェクトのクラスが必要です。このようなクラスのコードは次のとおりです(大きすぎないことを願っています)。

class SequenceView:

    def __init__(self, sequence, range_object=None):
        if range_object is None:
            range_object = range(len(sequence))
        self.range    = range_object
        self.sequence = sequence

    def __getitem__(self, key):
        if type(key) == slice:
            return SequenceView(self.sequence, self.range[key])
        else:
            return self.sequence[self.range[key]]

    def __setitem__(self, key, value):
        self.sequence[self.range[key]] = value

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

    def __iter__(self):
        for i in self.range:
            yield self.sequence[i]

    def __repr__(self):
        return f"SequenceView({self.sequence!r}, {self.range!r})"

    def __str__(self):
        if type(self.sequence) == str:
            return ''.join(self)
        elif type(self.sequence) in (list, tuple):
            return str(type(self.sequence)(self))
        else:
            return repr(self)

(これは約5分でまとめられたので、重要な場所で使用する前に、十分にテストしてください。)

使用法:

>>> p = list(range(10))
>>> q = SequenceView(p)[3:6]
>>> print(q)
[3, 4, 5]
>>> q[1] = -1
>>> print(q)
[3, -1, 5]
>>> print(p)
[0, 1, 2, 3, -1, 5, 6, 7, 8, 9]

*Python3の場合

于 2019-07-09T17:38:34.440 に答える
-3

「ビュー」に順番にアクセスする場合は、itertools.islice(..)を使用できます。詳細については、ドキュメントを参照してください

l = [1, 2, 3, 4, 5]
d = [1:3] #[2, 3]
d = itertools.islice(2, 3) # iterator yielding -> 2, 3

個々の要素にアクセスしてスライス内の要素を変更することはできません。リストを変更する場合は、isclice(..)を再度呼び出す必要があります。

于 2016-08-12T12:43:03.130 に答える