辞書ビューオブジェクトとは何ですか?


158

Python 2.7では、ディクショナリビューメソッドを使用できるようになりました。

今、私は次の長所と短所を知っています:

  • dict.items()(そしてvalueskeys):あなたが実際に結果を格納することができますので、リストを返し、
  • dict.iteritems() (など):ジェネレータを返すため、生成された各値を1つずつ反復できます。

何のためにdict.viewitems()(など)?それらの利点は何ですか?どのように機能しますか?結局のところ、ビューとは何ですか?

ビューは常に辞書からの変更を反映していると読みました。しかし、パフォーマンスとメモリの観点からはどのように振る舞いますか?長所と短所は何ですか?

回答:


157

辞書ビューとは、基本的にその名前のとおりです。ビューは、辞書のキーと値(またはアイテム)のウィンドウのようなものです。Python 3の公式ドキュメントからの抜粋です。

>>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
>>> keys = dishes.keys()
>>> values = dishes.values()

>>> # view objects are dynamic and reflect dict changes
>>> del dishes['eggs']
>>> keys  # No eggs anymore!
dict_keys(['sausage', 'bacon', 'spam'])

>>> values  # No eggs value (2) anymore!
dict_values([1, 1, 500])

(Python 2の同等の使用法dishes.viewkeys()dishes.viewvalues()ます。)

この例は、ビュー動的な特性を示しています。keysビューは、特定の時点でのキーのコピーではなく、キーを表示する単純なウィンドウです。それらが変更された場合、ウィンドウから見えるものも変更されます。この機能は状況によっては役立つ場合があります(たとえば、プログラムの複数の部分でキーのビューを操作して、必要になるたびにキーの現在のリストを再計算する必要がなくなります)。辞書のキーが変更された場合は注意してくださいビューを反復処理する際、イテレータの動作が適切に定義されていないため、エラーが発生する可能性があります。

一つの利点は、ということです探していると言う、キーの用途のみ、でメモリの小さな固定量とが必要とプロセッサ時間の小さくて一定量を、キーのリストのない創造がないように、他の一方で、Pythonの2(、多くの場合、Rajendran Tが引用しているように、不必要に新しいリストを作成します。これには、リストの長さに比例した量のメモリと時間がかかります)。窓の類推を続けるために、壁の後ろの風景を見たい場合は、そこに開口部を作るだけです(窓を作る)。キーをリストにコピーすることは、代わりに壁の風景のコピーをペイントすることに対応します。コピーは時間とスペースを要し、それ自体は更新されません。

要約すると、ビューは単に…辞書のビュー(ウィンドウ)であり、変更後も辞書の内容を表示します。これらはリストの機能とは異なる機能を提供します。キーのリストには特定の時点での辞書キーのコピーが含まれますが、ビューは動的であり、データをコピーする必要がないため、取得がはるかに高速です(キーまたは値)を作成するため。


6
+1。OK、それはキーの内部リストに直接アクセスすることとどう違うのですか?それはもっと速いですか、遅いですか?より効率的なメモリ?制限されていますか?あなたがそれを読んで編集することができるなら、それはこのリストへの参照を持っていることと全く同じように感じます。
e-satis、2012年

3
ありがとう。重要なのは、ビュー「キーの内部リスト」へのアクセスであることです(ただし、この「キーのリスト」はPythonリストではありませんが、正確にはビューです)。ビューは何もコピーしないため、Python 2のキー(または値やアイテム)のリストよりもメモリ効率が高くなります。それらは確かに「キーのリストへの参照」に似ています(リストは変更可能なオブジェクトであるため、「リストへの参照」は実際にはPythonでは単にリストと呼ばれることに注意してください)。また、ビューを直接編集することはできないことに注意してください。代わりに、辞書を編集すると、ビューに変更がすぐに反映されます。
Eric O Lebigot、2012年

3
わかりました。実装についてはまだはっきりしていませんが、現時点ではそれが最良の答えです。
e-satis 2012年

2
ありがとう。実際、この答えは主にビューのセマンティクスに関するものです。CPythonでのそれらの実装に関する情報はありませんが、ビューは基本的に正しい構造(キーまたは値、あるいはその両方)へのポインターであり、構造はディクショナリーオブジェクト自体の一部であると思います。
Eric O Lebigot、2012年

5
この投稿のサンプルコードはpython3からのものであり、python2.7で取得したものではないことを指摘する価値があると思います。
12

21

あなたが言及したようdict.items()に、無駄な(キー、値)ペアのディクショナリのリストのコピーを返し、無駄なものでありdict.iteritems()、ディクショナリの(キー、値)ペアのイテレータを返します。

次の例を見て、dictのインターレーターとdictのビューの違いを確認してください。

>>> d = {"x":5, "y":3}
>>> iter = d.iteritems()
>>> del d["x"]
>>> for i in iter: print i
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

これに対して、ビューはディクテーションの内容を表示するだけです。変更されたかどうかは関係ありません。

>>> d = {"x":5, "y":3}
>>> v = d.viewitems()
>>> v
dict_items([('y', 3), ('x', 5)])
>>> del d["x"]
>>> v
dict_items([('y', 3)])

ビューとは、単に辞書が現在どのように見えるかです。エントリ.items()を削除すると、古くなっ.iteritems()てエラーがスローされます。


素晴らしい例、ありがとう。ただし、v = d.items()である必要がありますv-d.viewitems()
rix

1
質問はPython 2.7に関するものなのでviewitems()、実際には正しいです(items()Python 3で正しく表示されます)。
Eric O Lebigot、2015

ただし、ビュー使用してディクショナリを変更している間、ビューを反復することはできません
Ioannis Filippidis 2017

18

ドキュメントを読んだだけで、次のような印象を受けます。

  1. ビューは「疑似セットのような」ビューであり、インデックス作成をサポートしていないため、ビューで実行できることは、メンバーシップをテストし、それらを反復処理することです(キーはハッシュ可能で一意であるため、キーとアイテムのビューはより多くの "それらは重複を含まないという点で「セットのような」)。
  2. それらを保存して、リストバージョンのように複数回使用できます。
  3. 基礎となるディクショナリを反映しているため、ディクショナリの変更はビューを変更し、ほぼ確実に反復の順序を変更します。そのため、リストのバージョンとは異なり、「安定」していません。
  4. これらは基礎となる辞書を反映しているため、ほぼ確実に小さなプロキシオブジェクトです。キー/値/アイテムをコピーするには、元の辞書を何らかの方法で監視し、変更が発生したときに複数回コピーする必要があります。これは不条理な実装です。そのため、メモリのオーバーヘッドは非常に少ないと予想されますが、辞書への直接アクセスよりも少し遅くなります。

したがって、キーのユースケースは、辞書を維持し、その間に変更を加えてキー/アイテム/値を繰り返し反復する場合だと思います。あなただけ回し、代わりにビューを使用することができますfor k, v in mydict.iteritems():for k, v in myview:。ただし、辞書を1回だけ反復する場合は、反復バージョンが望ましいと思います。


2
入手したいくつかの情報から賛否両論を分析するための+1。
e-satis、2012年

ビューにイテレータを作成しても、辞書が変更されるたびにイテレータが無効になります。これは、ディクショナリ自体のイテレータ(たとえばiteritems())と同じ問題です。では、これらの見解のポイントは何ですか?いつでも喜んでもらえますか?
Alfe、2017

@Alfeそうです、それは辞書の反復に関する問題であり、ビューはそれをまったく助けません。辞書の値を関数に渡す必要があるとします。を使用することもできますが.values()、コピー全体をリストとして作成する必要があるため、コストが高くなる可能性があります。あり.itervalues()ますが、それらを複数回使用することはできないため、すべての機能で動作するわけではありません。ビューは高額なコピーを必要としませんが、イテレータよりもスタンドアロンの値としてさらに役立ちます。しかし、それらはまだ反復と修正を同時に支援することを意図していません(本当にコピーが必要です)。
Ben

17

ビューのメソッドはリストを返します(リストのコピーではなく.keys().items()およびと比較して.values())。そのため、より軽量ですが、ディクショナリの現在の内容を反映します。

以下からのPython 3.0 - dictの方法は、ビューを返す-なぜ?

主な理由は、多くのユースケースでは完全に切り離されたリストを返す必要がなく、無駄が多いためです。コンテンツ全体をコピーする必要があります(多くの場合と多くない場合があります)。

キーを繰り返し処理するだけの場合は、新しいリストを作成する必要はありません。また、実際に別のリストとして(コピーとして)必要な場合は、ビューからそのリストを簡単に作成できます。


6
ビューメソッドは、リストインターフェイスに準拠しないビューオブジェクトを返します。
マシュートレバー

5

ビューを使用すると、基礎となるデータ構造にコピーせずにアクセスできます。リストを作成するのではなく動的であることに加えて、最も有用な使用法の1つはinテストです。値がdictにあるかどうか(キーか値のどちらか)を確認したいとします。

オプション1は、を使用してキーのリストを作成dict.keys()することです。これは機能しますが、明らかにより多くのメモリを消費します。辞書が非常に大きい場合?それは無駄です。

を使用viewsすると、中間リストなしで実際のデータ構造を反復できます。

例を使ってみましょう。私はランダムな文字列と数字の1000個のキーを持つ辞書を持っていて、私がk探したいキーです

large_d = { .. 'NBBDC': '0RMLH', 'E01AS': 'UAZIQ', 'G0SSL': '6117Y', 'LYBZ7': 'VC8JQ' .. }

>>> len(large_d)
1000

# this is one option; It creates the keys() list every time, it's here just for the example
timeit.timeit('k in large_d.keys()', setup='from __main__ import large_d, k', number=1000000)
13.748743600954867


# now let's create the list first; only then check for containment
>>> list_keys = large_d.keys()
>>> timeit.timeit('k in list_keys', setup='from __main__ import large_d, k, list_keys', number=1000000)
8.874809793833492


# this saves us ~5 seconds. Great!
# let's try the views now
>>> timeit.timeit('k in large_d.viewkeys()', setup='from __main__ import large_d, k', number=1000000)
0.08828549011070663

# How about saving another 8.5 seconds?

ご覧のように、viewオブジェクトを反復するとパフォーマンスが大幅に向上し、同時にメモリのオーバーヘッドが減少します。Set同様の操作を実行する必要がある場合は、これらを使用する必要があります。

:Python 2.7で実行しています


Python> = 3では、.keys()デフォルトでビューを返すと思います。ダブルチェックしたいかもしれません
Yolo Voe 2018

1
あなたが正しい。Python 3+は、リストではなくビューオブジェクトを頻繁に使用します。これにより、メモリ効率が大幅に向上します
チェンA.

1
これらのタイミング結果は非常に言って、しかし、かどうかをチェックされているk辞書の鍵の一つであるlarge_dと行われることを意図しているk in large_d他の言葉で(ビューを使用したのと本質的に同じ速おそらくあるのPythonで、k in large_d.keys()Python的ではなく、avoided-する必要がありますそのままですk in large_d.viewkeys())。
エリックOレビゴット

確かで有用な例を提供していただきありがとうございます。k in large_dは実際にはを大幅に上回るk in large_d.viewkeys()ため、おそらく回避する必要がありますが、これはにとって意味がありk in large_d.viewvalues()ます。
naught101 2018年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.