web-dev-qa-db-ja.com

Python(または任意の不変のデータ型)でタプルが必要なのはなぜですか?

私はいくつかのpythonチュートリアル(Dive Into Python、1つ))、およびPython.orgの言語リファレンスを読みました-言語にタプルが必要な理由がわかりません。

タプルにはリストまたはセットに比べてメソッドがありません。タプルをソートできるようにセットまたはリストに変換する必要がある場合、最初にタプルを使用する意味は何ですか?

不変性?

変数が最初に割り当てられたときとは異なる場所にメモリ内に存在する場合、なぜ誰もが気になりますか? Pythonにおける不変性のビジネス全体は、強調されすぎているようです。

C/C++では、ポインターを割り当てて有効なメモリを指す場合、使用する前にアドレスがnullでない限り、アドレスの場所は気にしません。

その変数を参照するたびに、ポインターがまだ元のアドレスを指しているかどうかを知る必要はありません。 nullをチェックして使用します(または使用しません)。

Pythonでは、文字列(またはTuple)を割り当ててxに割り当ててから、文字列を変更します。元のオブジェクトかどうか気にする必要があるのはなぜですか?変数が私のデータを指している限り、それだけが重要です。

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

xは引き続き必要なデータを参照しますが、そのIDが同じであるか異なるかを気にする必要があるのはなぜですか?

136
pyNewGuy
  1. 不変オブジェクトは、大幅な最適化を可能にします。これはおそらく、文字列もJavaで不変であり、まったく別個に開発されたもののPythonとほぼ同じ理由であり、真に機能する言語ではほぼすべてが不変である理由です。

  2. in Python、不変のもののみがハッシュ可能です(したがって、セットのメンバー、または辞書のキー)。これも最適化を可能にしますが、単なる「実質的な」(設計完全に変更可能なオブジェクトを格納するまともなハッシュテーブルは悪夢です-ハッシュ化するとすぐにすべてのコピーを取得するか、最後に参照を取得してからオブジェクトのハッシュが変更されたかどうかを確認する悪夢のどちらかです。

最適化の問題の例:

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop
118
Alex Martelli

上記の回答はどれも、タプルとリストの本当の問題を指摘していません。これは、多くの新しいPythonが完全に理解していないようです。

タプルとリストは異なる目的を果たします。リストには同種のデータが保存されます。次のようなリストを作成できます。

["Bob", "Joe", "John", "Sam"]

リストを正しく使用する理由は、リストがすべて同種のデータ、具体的には人の名前であるためです。しかし、このようなリストを取る:

["Billy", "Bob", "Joe", 42]

そのリストは一人のフルネームと年齢です。それはデータの一種ではありません。その情報を保存する正しい方法は、タプルまたはオブジェクトのいずれかです。いくつかあるとしましょう:

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

タプルとリストの不変性と可変性は、主な違いではありません。リストは、ファイル、名前、オブジェクトなど、同じ種類のアイテムのリストです。タプルは、さまざまなタイプのオブジェクトのグループです。それらはさまざまな用途があり、多くのPythonコーダーはタプルが何のためにあるのかリストを乱用します。

しないでください。


編集:

このブログ投稿は、私がこれをやったよりも良いと思う理由を説明していると思います: http://news.e-scribe.com/397

40
Grant Paul

タプルをソートできるようにセットまたはリストに変換する必要がある場合、最初にタプルを使用する意味は何ですか?

この特定のケースでは、おそらくポイントはありません。これは、タプルの使用を検討するケースの1つではないため、問題ではありません。

あなたが指摘するように、タプルは不変です。不変の型を持つ理由はタプルに適用されます:

  • コピー効率:不変オブジェクトをコピーするのではなく、エイリアスを作成できます(変数を参照にバインドします)
  • 比較効率:参照によるコピーを使用している場合、コンテンツではなく場所を比較することで2つの変数を比較できます。
  • インターン:不変値のコピーを最大1つ保存する必要があります
  • 並行コード内の不変オブジェクトへのアクセスを同期する必要はありません
  • constの正確性:一部の値は変更できません。これは(私にとって)不変型の主な理由です。

特定のPython=の実装では、上記の機能のすべてを利用できない場合があります。

辞書キーは不変でなければなりません。さもないと、キーオブジェクトのプロパティを変更すると、基礎となるデータ構造の不変式が無効になる可能性があります。したがって、タプルは潜在的にキーとして使用できます。これはconstの正確性の結果です。

Introducing tuples "、from Dive Into Python 」も参照してください。

22
outis

オブジェクトを辞書キーとして使用したい場合があります

価値があるものとして、最近タプル(2.6+)がindex()およびcount()メソッドを増やしました

15
John La Rooy

同じ基本的なデータ構造(配列)に対して2つの完全に分離した型があるのは、厄介な設計であることが常にわかっていますが、実際の問題ではありません。 (すべての言語にはイボがあり、Pythonが含まれていますが、これは重要なものではありません。)

変数が最初に割り当てられたときとは異なる場所にメモリ内に存在する場合、なぜ誰もが気になりますか? Pythonにおける不変性のビジネス全体は、強調されすぎているようです。

これらは異なるものです。可変性は、メモリに格納されている場所とは関係ありません。 それが指すものは変更できないことを意味します。

Pythonオブジェクトは、作成後に位置を変更することはできません(変更可能かどうかは関係ありません)。 (より正確には、id()の値は変更できません。実際には同じことです。)可変オブジェクトの内部ストレージは変更できますが、それは実装の詳細です。

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

これは変数の変更(「変更」)ではありません。同じ名前の新しい変数を作成し、古い変数を破棄しています。変化する操作と比較してください:

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

他の人が指摘したように、これは辞書のキーとしての配列、および不変性を必要とする他のデータ構造を使用することを可能にします。

辞書のキーは完全に不変である必要はありません。キーとして使用される部分のみが不変である必要があります。いくつかの用途では、これは重要な違いです。たとえば、ユーザーを表すクラスを作成し、同等性とハッシュを一意のユーザー名で比較することができます。その後、クラスに他の変更可能なデータを掛けることができます-「ユーザーはログインしています」などPythonでこれはあまり必要ありません。キーは「不変」である必要があると主張している人がいるので、それを指摘するだけです。これは部分的に正しいだけです。しかし、私はこれをC++マップとセットで何度も使用しました。

9
Glenn Maynard

Gnibblerがコメントで提供したように、Guidoには 意見 があり、これは完全に受け入れ/評価されていません:「リストは同種データ用、タプルは異種データ用です」。もちろん、反対者の多くは、これをリストのすべての要素が同じ型であるべきだと解釈しました。

others が過去に持っていたものと違って、私はそれを異なって見るのが好きです:

blue= 0, 0, 255
alist= ["red", "green", blue]

Type(alist [1])!= type(alist [2])であっても、alistは同種であると見なしていることに注意してください。

要素の順序を変更でき、コードに問題がない場合(仮定、たとえば「ソートする必要がある」を除く)、リストを使用する必要があります。そうでない場合(上のタプルblueのように)、タプルを使用する必要があります。

7
tzot

それらは、渡すオブジェクトが変更されないことを呼び出し側に保証するため、重要です。これを行う場合:

a = [1,1,1]
doWork(a)

呼び出し元は、呼び出し後のaの値を保証しません。しかしながら、

a = (1,1,1)
doWorK(a)

これで、このコードの呼び出し元またはリーダーとして、あなたはaが同じであることを知っています。このシナリオでは常にリストのコピーを作成して渡すことができますが、より意味のある言語構造を使用する代わりにサイクルを無駄にしています。

6
Matthew Manela

質問(およびフォローアップコメント)は、割り当て中にid()が変更されるかどうかに焦点を当てています。違いそのものではなく、不変オブジェクトの置換と可変オブジェクトの変更の違いによるこの後続の効果に注目することは、おそらく最良のアプローチではありません。

続行する前に、以下に示す動作がPythonに期待するものであることを確認してください。

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

この場合、a1のみに新しい値が割り当てられていても、a2の内容が変更されました。以下とは対照的です:

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

後者の場合、リストの内容を更新するのではなく、リスト全体を置き換えました。 タプルなどの不変の型では、これが許可される唯一の動作です。

なぜこれが重要なのですか?あなたが辞書を持っているとしましょう:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new Tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

タプルを使用すると、ディクショナリはキーを「その下から」別の値にハッシュするアイテムに変更することから安全です。これは、効率的な実装を可能にするために重要です。

1
Charles Duffy

あなたは here を見ることができます

1
ghostdog74