Pythonの5つの嫌われ所


いちPythonファンとして思うところを書いてみた。

1. listにdictのget相当(範囲外でNoneを得る)がない

確かにその通りである。dict.get相当のメソッドはリストにも欲しい。しかし、Rubyみたいにデフォルトでnilを返すようにするのは、意図していない場合でもエラーになってくれないので困る。Pythonのポリシーとしては、範囲外の要素にアクセスするにはプログラムに明示的にそれを書かないといけないということを強制できるので安全性が高いと思う。

L = [...]
if elem not in L:  # 範囲外の要素である場合
    ....

if idx not in range(len(L)):  # 範囲外のインデックスである場合(負の値は考慮していない)
    ....

2. (x)rangeが終端を含まない 「for month in xrange(1,13)」とか

分かりづらいし、インデックスを使うときはenumerate()を使えという主張だが、C風の0から始まるインデックスでは終端を含まないのが自然であり、インデックスの幅がゼロの場合と1以上の場合で場合分けの必要性がないので、むしろそれで正解。インデックスを使いたいときも、たまにある。
但し、Rubyみたいに終端を含むものと含まないものの両方あった方が良い。あと、Rubyの 0..10 や 0...10 みたいに、短く直感的に書けるのはすばらしい!Pythonはrange()みたいな関数であるし、構文が関数ベースなのでカッコが重なりすぎて見づらい。見づらいからmapでなくリスト内包表記を使用したり、わざと複数の行に分けて書いたりする。確かにPythonの嫌なところだ。

3. (Cの)static変数相当がない

クロージャを使用すれば回避できるが、大げさになりすぎる。確かにもっと簡易的に書きたい。

def foo(i):
    n = [i]
    def cnt():
        n[0] += 1
        return n[0]
    return cnt

f = foo(0)
f()
# 1
f()
# 2

4. メソッドだったり関数だったり。len()とかsort()とか

reversed()はイテレータを返し、sorted()はリストを返す。reverse()はメソッドで、reversed()は関数。統一感がない。全てメソッドにして、in place(破壊)しないようにせよという意見。
Python3000ではrange()もイテレータ(ジェネレータ?)を返すし、sorted()もそうなって統一されるであろう。但し、互換性の問題もあり、関数がメソッドになる可能性は少ないと思うが、私は気にならない。例えば、L.len()となっても、どっちみち()は省略できないし、L.len().times()みたいなメソッドをつなげていく書き方はPythonでは汚い。

5. lambdaが1行式しか書けない

lambdaに文が書けないし、式も1つしか書けないのが嫌だという意見。激しく同意。しかし、Pythonは式と文を明確に区別するし、区別がかなり重要。文は副作用を持つのでlambdaやリスト内包表記の考え方と合わない。利便性より分かりやすさを優先させているので仕方ないというところだろう。

あと、コンテストや使い捨てコードみたいな用途ではRubyのコードブロックみたいな機能は短く書けるのでうれしいが、中規模や大規模開発では見た瞬間に分かりやすいプログラムの方がよいと思うので、極端に短く書ける必要はないかもしれない。L.map {...} でなく、for e in L: ... でも十分。