変数に関数と同じ名前をつけてはいけない

Project Eulerのある問題を解いてるときにはまったのでメモ。
整数と文字列からスコアを計算してその合計を求める問題なんだけど、途中でこういうコードを書いた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum += score

print sum

name_score がスコアを計算する関数で、6行目と16行目(forブロックの中)の2箇所で呼び出している(6行目はちゃんと動くかの確認用)。ところが、これを実行するとこうなる。

^o^ > python a.py
36
Traceback (most recent call last):
  File "a.py", line 16, in 
    score = name_score(n, name)
  File "a.py", line 2, in name_score
    v = sum(map((lambda c: ord(c) - 64), str))
TypeError: 'int' object is not callable

1回目の呼び出し(6行目)ではちゃんとスコアが計算されているのに、2回目(forブロックの中)ではエラーになって、’int’オブジェクトは callable じゃないと怒られる。
これで小一時間ほども悩んだけど、問題は13行目のsumという変数と2行目で使っているsum関数の名前が同じことだった。

Pythonでは、定義した関数をほかの変数に代入することができる。

>>> def hello(name):
...     print "Hello,", name
...
>>> hello("Andy")
Hello, Andy
>>> greeting = hello
>>> greeting("Bill")
Hello, Bill

それはいいんだけど、逆に関数名にほかの値、たとえば整数を代入することもできてしまう。

>>> hello = 1
>>> hello("Charlie")
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'int' object is not callable

hello には 1 を代入してしまったので、関数呼び出しのつもりがエラーになってしまっている。上で紹介したコードでも13行目で sum に 0 を代入しているので、同じことが起こったわけだ。
結局、変数名を変えたら期待通りに動いてくれた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum_of_score = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum_of_score += score

print sum_of_score
^o^ > python b.py
36
282

Python では関数もオブジェクトだからかもしれないけど、普通の変数名と関数名の間に区別がないようだ。

今日の結論:変数に関数と同じ名前をつけてはいけない。