Pythonにタプルの理解がないのはなぜですか?


337

ご存知のように、リスト内包表記があります。

[i for i in [1, 2, 3, 4]]

次のような辞書の理解があります

{i:j for i, j in {1: 'a', 2: 'b'}.items()}

だが

(i for i in (1, 2, 3))

tuple理解ではなくジェネレータになってしまいます。何故ですか?

私の推測では、a tupleは不変ですが、これは答えではないようです。


15
セット内包表記もあります-これは、
口述内包表記に

3
コードに構文エラーがあります:ある{i:j for i,j in {1:'a', 2:'b'}}はずです{i:j for i,j in {1:'a', 2:'b'}.items()}
Inbar Rose

@InbarRose指摘してくれてありがとう-.-
Shady Xu

後世のために、Pythonチャット
Inbar Rose

回答:


467

ジェネレータ式を使用できます。

tuple(i for i in (1, 2, 3))

括弧は、…ジェネレータ式ですでに使用されています。


15
この議論により、リスト内包表記も不要であると言えます list(i for i in (1,2,3))。私は本当にそれは単純な構文がないためだと思います(または少なくとも誰もそれを考えていません)
mgilson

79
リストまたはセットまたはディクトリ内包表記は、特定のタイプを出力するジェネレータ式を使用するための単なる構文糖衣です。list(i for i in (1, 2, 3))リストをset(i for i in (1, 2, 3))出力し、セットを出力するジェネレータ式です。それは、理解構文が不要であることを意味しますか?おそらくそうではないかもしれませんが、それは非常に便利です。代わりにタプルが必要になるまれなケースでは、ジェネレーター式が実行し、明確であり、別のブレースまたはブラケットの発明を必要としません。
Martijn Pieters

16
答えは明らかにタプルの構文と括弧が曖昧だからです
Charles Salvia

19
内包表記を使用する場合とコンストラクター+ジェネレーターを使用する場合の違いは、パフォーマンスを重視する場合は微妙です。内包は、コンストラクタに渡されるジェネレータを使用する場合と比較して、より高速な構築をもたらします。後者の場合は、関数を作成して実行するため、Pythonでは関数が高価になります。 [thing for thing in things]は、よりもはるかに高速にリストを作成しますlist(thing for thing in things)。タプルの理解は役に立たないでしょう。tuple(thing for thing in things)レイテンシの問題tuple([thing for thing in things])があり、メモリの問題がある可能性があります。
ジャスティンターナーアーサー

9
@MartijnPieters、あなたは潜在的に言い換えることができますA list or set or dict comprehension is just syntactic sugar to use a generator expressionか?これは、これらを同等の手段であると見なす人々に混乱を引き起こしています。最終製品が同じであっても、実際にはプロセスが異なるため、技術的に構文上の砂糖ではありません。
2018

77

レイモンドヘッティンガー(Pythonコア開発者の1人)は、最近のツイートでタプルについてこれを述べています:

#pythonのヒント:通常、リストはループ用です。構造体のタプル。リストは同種です。タプルは異種です。可変長のリスト。

これは(私にとって)シーケンス内の項目が、ジェネレーターによって生成されるのに十分に関連している場合、リストである必要があるという考えをサポートしています。タプルは反復可能で、単に不変のリストのように見えますが、実際にはPythonでC構造体に相当するものです。

struct {
    int a;
    char b;
    float c;
} foo;

struct foo x = { 3, 'g', 5.9 };

Pythonになる

x = (3, 'g', 5.9)

26
不変性プロパティは重要ですが、通常はリストを使用する場合にタプルを使用するのに十分な理由になります。たとえば、dictのキーとして使用する5つの数字のリストがある場合、タプルが適しています。
pavon

これはレイモンドヘッティンガーからのすばらしいヒントです。タプルレコードへの変換に関心のあるattrsを反復することにより、別の構造体(おそらくより大きなもの)をより小さなものにアンパックするなど、タプルコンストラクターをジェネレーターと共に使用する使用例があると私はまだ言います。
デイブ2016

2
@dave operator.itemgetterその場合はおそらくそのまま使用できます。
chepner

@chepner、なるほど。それは私が言っていることにかなり近いです。呼び出し可能オブジェクトを返すので、tuple(obj[item] for item in items)直接使用するだけの場合と比較して、あまりメリットが見られない場合にのみ実行できます。私の場合、これをリスト内包表記に組み込んで、タプルレコードのリストを作成しました。コード全体でこれを繰り返し行う必要がある場合、itemgetterは見栄えがします。おそらくitemgetterはどちらにせよもっと慣用的でしょうか?
デイブ2016

私は、frozensetとsetの関係を、タプルとリストの関係に似ています。それは異質性についてではなく、不変性についてです。frozensetsとtupleは、それらの可変性のために、辞書、リスト、セットのキーになることはできません。
ポリグロット

56

Python 3.5以降では*splat展開構文を使用してジェネレータ式を展開することもできます。

*(x for x in range(10)),

2
これはすばらしい(そしてうまくいく)のですが、文書化されている場所はどこにもありません!リンクはありますか?
felixphew 2017

8
注:実装の詳細として、これは基本的には同じですtuple(list(x for x in range(10)))コードパスは同じですが、どちらもを構築listしますが、最後のステップは、tupleからを作成し、出力listを破棄することです必要とされている)。一時的なペアを実際に回避しないことを意味します。listtuple
ShadowRanger

4
@ShadowRangerのコメントを拡張するために、ここでは、splat + tupleリテラル構文が、ジェネレーター式をtupleコンストラクターに渡すよりもかなり遅いことを示す質問があります。
Lucubrator

私はこれをPython 3.7.3で試していますが、*(x for x in range(10))動作しません。わかりますSyntaxError: can't use starred expression here。しかしtuple(x for x in range(10))動作します。
Ryan H.

4
@RyanH。最後にコンマを置く必要があります。
czheo

27

別のポスターでmacm述べたように、ジェネレーターからタプルを作成する最も速い方法はtuple([generator])です。


性能比較

  • リスト内包表記:

    $ python3 -m timeit "a = [i for i in range(1000)]"
    10000 loops, best of 3: 27.4 usec per loop
  • リスト内包からのタプル:

    $ python3 -m timeit "a = tuple([i for i in range(1000)])"
    10000 loops, best of 3: 30.2 usec per loop
  • ジェネレーターからのタプル:

    $ python3 -m timeit "a = tuple(i for i in range(1000))"
    10000 loops, best of 3: 50.4 usec per loop
  • 開梱からのタプル:

    $ python3 -m timeit "a = *(i for i in range(1000)),"
    10000 loops, best of 3: 52.7 usec per loop

私のバージョンのpython

$ python3 --version
Python 3.6.3

したがって、パフォーマンスが問題にならない限り、常にリスト内包からタプルを作成する必要があります。


10
注:tupleof listcompには、ファイナルtupleとの合計サイズに基づいてピーク時のメモリ使用量が必要listです。tuplegenexprの速度は遅くなりますが、tuple一時的な料金ではなく、最終的な料金のみを支払うことを意味しますlist(genexpr自体はおおよそ固定されたメモリを占有します)。通常は意味がありませんが、関連するサイズが非常に大きい場合は重要です。
ShadowRanger

25

理解は、アイテムをループまたは反復し、それらをコンテナーに割り当てることによって機能します。タプルは割り当てを受け取ることができません。

タプルが作成されると、追加、拡張、または割り当てを行うことはできません。タプルを変更する唯一の方法は、そのオブジェクトの1つをそれ自体に割り当てることができる場合(非タプルコンテナー)です。タプルはその種類のオブジェクトへの参照のみを保持しているためです。

また、タプルには、tuple()任意のイテレータを指定できる独自のコンストラクタがあります。つまり、タプルを作成するには、次のようにします。

tuple(i for i in (1,2,3))

9
いくつかの点では同意しますが(リストで十分なので必要ではない)、他の点では同意しません(理由は不変であるためです)。ある意味では、不変オブジェクトを理解する方が理にかなっています。誰がしlst = [x for x in ...]; x.append()ますか?
mgilson 2013年

@mgilson私がそれが私が言ったことにどのように関連するのかわかりませんか?
インバーローズ

2
@mgilsonは、タプルが不変である場合、つまり、基礎となる実装タプルを「生成」できないことを意味します(「生成」は、一度に1つのピースを構築することを意味します)。不変とは、3ピースの1つを変更して4ピースの1つを構築できないことを意味します。代わりに、生成用に設計されたリストを作成してタプルの「生成」を実装し、最後のステップとしてタプルを作成して、リストを破棄します。言語はこの現実を反映しています。タプルはCの構造体と考えてください。
スコット

2
内包表記が返されるまでタプルを使用できないため、内包表記の構文シュガーがタプルに対して機能することは妥当です。事実上、変更可能ではなく、タプル内包表記は文字列追加のように動作します。
uchuugaka

12

私の推測では、ブラケットが不足していて、「醜い」構文を追加するのに十分ではないと思いました...


1
山括弧は未使用です。
uchuugaka

@uchuugaka-完全ではない。それらは比較演算子に使用されます。それはおそらくまだ曖昧さなしで行うことができますが、おそらく努力する価値はありません...
mgilson

3
@uchuugaka注目に値するが{*()}、醜いが、空のリテラルとして機能する!
MIライト

1
ああ。美的観点から、私はset():)に部分的だと思います
mgilson

1
@QuantumMechanic:ええ、それがポイントです。アンパックの一般化により、空の「セットリテラル」が可能になりました。{*[]}は他のオプションよりも厳密に劣ることに注意してください。空の文字列とempty tupleは不変であり、シングルトンであるため、emptyを作成するために一時変数は必要ありませんset。対照的に、空listはシングルトンではないため、実際にそれを構築しset、それを使用してを構築し、次にそれを破棄する必要があります。片目猿のオペレーターが提供する些細なパフォーマンス上の利点は失われます。
ShadowRanger 2018年

8

タプルはリストのように効率的に追加することはできません。

したがって、タプル内包表記では、リストを内部で使用してからタプルに変換する必要があります。

これは、今行っていることと同じです。tuple([comprehension])


3

括弧はタプルを作成しません。別名one =(two)はタプルではありません。唯一の方法は、one =(2つ)またはone = tuple(2つ)です。したがって、解決策は次のとおりです。

tuple(i for i in myothertupleorlistordict) 

いいね。それはほとんど同じです。
uchuugaka

-1

わかりやすくするためだと思います。あまりにも多くの異なる記号で言語を混乱させたくないのです。また、tuple内包表記は必要ありません。リスト内包表記とは異なり、dict内包表記とは異なり、速度の違いは無視できる代わりにリストを使用できます。


-2

リスト内包からタプルを生成できます。次のコードは、2つの数値を順番にタプルに追加し、0〜9の数値からなるリストを提供します。

>>> print k
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> r= [tuple(k[i:i+2]) for i in xrange(10) if not i%2]
>>> print r
[(0, 1), (2, 3), (4, 5), (6, 7), (8, 9)]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.