2016年1月11日

sum()関数

ビルトイン関数の1つである sum() のあまり知られていないと思われる仕様について紹介します。

sum() はその名の通り引数の合計を計算する関数です。
最も典型的な使い方は以下のようになります。

>>> c = [1, 2, 3]
>>> sum(c)
6

あまり知られていませんが、sum() は start という第二引数を持っており、そのデフォルト値は 0(int) です。
上例での sum() の中で実際に行われている処理は以下のようになります。
# sum(c)の実処理
>>> ((0 + 1) + 2) + 3
6
第二引数 start は最初の +演算の左辺値として使われます。第二引数を指定した例は以下のようになります。
>>> c = [1, 2, 3]
>>> sum(c, 4)
10

# sum()の実処理
# ((4 + 1) + 2) + 3


では、sum() の引数として二重リストを渡すとどうなるでしょうか。
>>> c = [[1, 2], [3], [4, 5]]
>>> sum(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'list'

# sum()の実処理
# ((0 + [1, 2]) + [3]) + [4, 5]
int と list は +演算できないというエラーになりました。実処理の方に目をやると、当然の結果と言えますね。

ここで、第二引数にリストを指定すると、演算が可能となります。
第二引数として空リストを渡すと、二重リストのフラット化処理を一文で実現できます(※ 残念ながら動作速度は早くありません。リストのサイズが大きい場合には他の手段を用いるべきです)。

>>> sum(c, [])
[1, 2, 3, 4, 5]

# sum()の実処理
# (([] + [1, 2]) + [3]) + [4, 5]


さらに、sum() の第一引数として文字列のリストを、第二引数として空文字列を渡すとどうなるでしょうか。上のリストの例をから考えると、文字列を結合できそうですが、、、。
>>> c = ['Monty', ' ', 'Python']
>>> sum(c, '')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: sum() can't sum strings [use ''.join(seq) instead]
sum() は文字列に対応していない、というエラーになりました。

この sum() の挙動は、Python のいわば超法規的な仕様といえます。
自然な実装という観点からは、sum() は引数の型を意識すること無く +演算を呼ぶべきです。しかし、頻繁に使われる文字列の結合に関しては、より高速な ''.join() を使わせるために、特例としてエラーが発生するようになっています。
プログラム言語の仕様というと理論が先に立っているものが多いので、このような実を取る仕様は意外な印象を受けますね。

0 件のコメント:

コメントを投稿