Python の tuple のリテラル表記について考える
id:t2y-1979 のパーフェクトPythonというエントリの一節、
要素が1つの場合には tuple である事が解らなくならないように、(1,) のように末尾にカンマを記述します。
これは少し誤解を招く表現です。というのは、分からなくならないようにこのような記述をするのではなく、tuple の構文はカンマで区切るものであり、カッコは人間が分かりやすいように付けているものだからです。
というのを見て、おや?と思った。
tuple の括弧って「人間がわかりやすいように付けているもの」なんだっけ? と。
たとえばこんな場合
最初に頭に浮かんだのは関数の呼び出しの場合。件のエントリでも例としてあげられているが
>>> type((1,)) <type 'tuple'>
のように関数の引数に tuple を渡す場合は括弧が必須になる。
もし、括弧が「人間がわかりやすいように付けているもの」なので省略可能だとすると type(1,) のようにも書けるはずだ。
だが、実際にこれを実行すると
>>> type(1,) <type 'int'>
となる。関数呼び出しをする際、末尾のカンマは tuple リテラルの宣言とは解釈されない。
それに func((1, 2, 3), 4) というのを括弧なしで記述するのは難しいだろう。
また、list の中に tuple を入れる場合も括弧は必要とされる。[(1, 2), (3, 4)] と書かないと list の中に tuple を宣言することができない。
字面を見たらそのまんまなんだけど、単にカンマで区切るだけでは tuple と list の境目がわからない。
というわけで、括弧は「機械(パーサ)が解釈する上でも必要なもの」だと思われるので、
「人間がわかりやすいように付けているもの」という意見には違和感を感じる。
そう考えると「tuple の構文は括弧とカンマでくくられたもの。ただし場面によっては括弧は省略できる」と表現するのがよいのではないだろうか。
そもそも多値と tuple リテラルって別物じゃないの?
次に、もうひとつ別のことを考えた。
括弧を省略した tuple って、多値を表すときに利用される syntax sugar なんじゃないか、と。
関数の中で return 1, 2, 3 と書いた時に、返り値として tuple が渡って行くけど、
これは「tuple をリテラルで書いている」のではなくて「多値を表現している」と考えたほうが自然だ。
また、a, b, c = 1, 2, 3 のような多重代入の場面でも「tuple をリテラルで書いている」という意図はないのではないだろうか。
これは、実装の効率や都合の関係で、多値を返す場合や多重代入の実現に tuple を利用しているのではないかという仮説だ。
Ruby でも多値を取り入れる際に、その実装として Array を使っている。(当時、matz は別のものを使おうか悩んでいたようだけど)
憶測を言わずに原典にあたれ
多値と tuple リテラルって別の由来じゃないの? ともにょもにょ考えていたが、
どちらにせよ tuple データが得られるのは変わらないんだし…ともにょもにょ考えていたのだが、
いろいろググっていたら最後に Python の言語リファレンスにたどり着いた。
その一節、オブジェクト、値、および型 の説明にこんな記載がある。
タプル型 (tuple)
タプルの要素は任意の Python オブジェクトにできます。二つまたはそれ以上の要素からなるタプルは、個々の要素を表現する式をカンマで区切って構成します。単一の要素からなるタプル (単集合 ‘singleton’) を作るには、要素を表現する式の直後にカンマをつけます (単一の式だけではタプルを形成しません。これは、式をグループ化するのに丸括弧を使えるようにしなければならないからです) 。要素の全くない丸括弧の対を作ると空のタプルになります。
ちゃんと読むと、個々の要素を表現する式をカンマで区切ってと記載されている。
原文ではTuples of two or more items are formed by comma-separated lists of expressions.という表現。
つまり、Python の tuple の定義は「式をカンマで区切ったもの」ということだ。
この定義には括弧の有無は関係ないようだ。
というわけで、僕の思いついた「多値が tuple 化されるのは syntax sugar で、tuple リテラルとは異なるもの」という仮説は誤りで、
単に return のところに tuple リテラルを書いているというもののようだ。
もちろん、途中で書いた「tuple の構文は括弧とカンマでくくられたもの。ただし場面によっては括弧は省略できる」というのも間違い。
というわけで、僕のひとり相撲は幕を閉じたのであるちゃんちゃん。
結論:ちゃんとリファレンス読みましょう。
おまけ
途中でちゃんと tuple リテラルになってるか調べるため、dis モジュールを使って動きを見たりしてました。
使い方忘れてちょっと戸惑ったので、メモ代わりに載せておきます。
>>> def foo(): ... return 1, 2, 3, 4 ... >>> foo() (1, 2, 3, 4) >>> >>> from dis import dis >>> dis(foo) 2 0 LOAD_CONST 5 ((1, 2, 3, 4)) 3 RETURN_VALUE
ちゃんと LOAD_CONST で tuple を読み込んでますね。