modalsoul’s blog

これは“失敗”と呼べるかもしれないが、ぼくは“学習体験”と呼びたい

Pythonのタプルでdestructuring bindingっぽい何か(unpacking assignment)でハマった

SQLArchemyを使ったアプリケーションで↓なコードがあった(詳細は割愛)

(age,) = db_session.query(User.age).filter(User.id == input.id).one()

右辺がUser.ageをラップしたなにかなのはわかるんだけど、左辺がよくわからない


これと似たようなもので、Scalaではdestructuring bindingというのがあって、

scala> case class User(age:Int, name:String)
defined class User

scala> val user = User(38, "Valentino Rossi")
user: User = User(38,Valentino Rossi)

scala> val User(age, _) = user
age: Int = 38

のように、右辺のタプルを展開して、左辺の変数に代入される


これと同じことなんだろうと思ったんだけど、左辺でage以外を握り潰しているっぽく見える書き方がよくわからない

(age,)のカンマで終わってるのが、Scalaでいうval User(age, _)ワイルドカードのような働きをしている?

でも、これだと

>>> user = (38, "Valentino Rossi")
>>> (age,) = user
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack (expected 1)

となって、握りつぶせていない

よくよく調べてみると、これはunpacking assignmentと呼ばれるもので、右辺がリストなどのコレクションの場合、左辺にカンマ区切りで変数を並べると、左辺の各変数に対応する順で右辺の値が代入されるようだ

そして、今回のケースで混乱の素になったのが、左辺は単要素タプルだということ

Pythonのリファレンスでタプル型を確認すると、

3. データモデル — Python 3.6.3 ドキュメント

タプル型 (tuple)

タプルの要素は任意の Python オブジェクトです。二つ以上の要素からなるタプルは、個々の要素を表現する式をカンマで区切って構成します。単一の要素からなるタプル (単集合 'singleton') を作るには、要素を表現する式の直後にカンマをつけます (単一の式だけではタプルを形成しません。これは、式をグループ化するのに丸括弧を使えるようにしなければならないからです)。要素の全くない丸括弧の対を作ると空のタプルになります。

とあり、今回のケースではageが単一の要素で、その直後にカンマがつくことでタプルになっている。そして、右辺も単要素のタプルであり、その値がageに代入されているということ

左辺でage以外を握り潰すことなんかしていなく、そもそも握り潰す値がなかったんや!

なので、右辺が2要素からなるタプルの場合、ValueErrorとなった


仕組みは理解できたからいいんだけど、こういうケースがggりずらくてつらくない?おれはつらい