1. ホーム
  2. python

[解決済み] Pythonでコピーを生成せずにリストをスライスする

2023-01-22 20:18:32

質問

次のような問題があります。

整数のリストが与えられたとき L があるとき、すべてのサブリスト L[k:] for k in [0, len(L) - 1] , コピーを生成することなく .

Pythonでこれを達成するにはどうすればよいですか?どうにかしてバッファオブジェクトで?

どのように解決するには?

短い答え

リストをスライスしても、リスト内のオブジェクトのコピーは生成されず、それらへの参照がコピーされるだけです。それが質問されたことに対する答えです。

長い答え

変更可能な値と変更不可能な値に対するテスト

まず、基本的な主張を検証してみましょう。整数のような不変のオブジェクトの場合でも、参照だけがコピーされることを示すことができます。ここに3つの異なる整数オブジェクトがあり、それぞれ同じ値を持っています。

>>> a = [1000 + 1, 1000 + 1, 1000 + 1]

これらは同じ値を持っていますが、それぞれ異なる id s:

>>> map(id, a)
[140502922988976, 140502922988952, 140502922988928]

それらをスライスしても、参照は同じままです。新しいオブジェクトは作成されていません。

>>> b = a[1:3]
>>> map(id, b)
[140502922988952, 140502922988928]

同じ値で異なるオブジェクトを使用することは、コピー処理が を介することなく -- を気にせず、ただ直接参照をコピーしていることがわかります。

mutableな値でテストしても、同じ結果になります。

>>> a = [{0: 'zero', 1: 'one'}, ['foo', 'bar']]
>>> map(id, a)
[4380777000, 4380712040]
>>> map(id, a[1:]
... )
[4380712040]

残りのメモリのオーバーヘッドを調べる

もちろん、リファレンス 自体 はコピーされます。64 ビットのマシンでは、それぞれ 8 バイトのコストがかかります。そして各リストは 72 バイトのそれ自身のメモリ オーバーヘッドを持ちます。

>>> for i in range(len(a)):
...     x = a[:i]
...     print('len: {}'.format(len(x)))
...     print('size: {}'.format(sys.getsizeof(x)))
... 
len: 0
size: 72
len: 1
size: 80
len: 2
size: 88

ジョー・ピンソノーとして は私たちを思い起こさせます。 というように、このオーバーヘッドが加算されます。また、整数オブジェクト自体はそれほど大きくなく、参照の 3 倍程度の大きさです。そのため、絶対的な意味ではメモリを節約できますが、漸近的には、同じメモリに複数のリストを "view"できるようにするのがよいかもしれません。

ビューを使用したメモリの節約

残念ながら、Pythonはリストに"view"されたオブジェクトを生成する簡単な方法を提供しません。というか、quot;fortunately" と言うべきかもしれません! これは、スライスがどこから来たかを気にする必要がないことを意味します; オリジナルへの変更はスライスに影響を与えません。全体として、これはプログラムの動作に関する推論をより簡単にします。

ビューで作業することで本当にメモリを節約したいのであれば numpy 配列の使用を検討してください。をスライスすると numpy 配列のスライスでは、メモリはスライスと元の配列の間で共有されます。

>>> a = numpy.arange(3)
>>> a
array([0, 1, 2])
>>> b = a[1:3]
>>> b
array([1, 2])

を変更するとどうなるか? a を修正して、もう一度 b ?

>>> a[2] = 1001
>>> b
array([   1, 1001])

しかし、これは、あるオブジェクトを修正するときに、不注意で別のオブジェクトを修正しないことを確認しなければならないことを意味します。これは numpy を使うときのトレードオフです。コンピュータの仕事は減り、プログラマーの仕事は増えます。