はじめに
Pythonでは変数名と関数名の名前空間が分かれていません。こういうのをLISP-1形式というらしいですがそもそもPythonの場合、関数名とは関数オブジェクトを指す変数です。分けるという発想自体がありません。
本編に入る前に私が前にやらかしたボケをご紹介しましょう。
>>> import json
>>> json = json.dumps({'abc': 123})
>>> json = json.dumps({'def': 456})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'dumps'
あれ?(。´・ω・)?
はいそうです。関数と同様、モジュールも名前空間は分離されてません。JSON代入する変数だからとjsonって名前にするとモジュール参照している変数が上書きされます。大文字小文字は区別してくれるので以下のように書きましょう。
>>> import json
>>> JSON = json.dumps({'abc': 123})
・・・冗談です。こんな書き方してるやつ見かけたらグーで殴ります。
前置き長すぎるのでそろそろ本題に入ります。
Pythonの名前検索とビルトイン関数
Pythonが名前を探す際には次の順番で探されます。
- ローカル変数
- グローバル変数
- ビルトイン関数
クロージャの話もありますが本筋じゃないので省略。
というわけで、以下のように書くと簡単にビルトイン関数をつぶせます。(後述するように正確に言うとつぶす=その関数が完全に使えなくなるわけではないのですが感覚的な意味でつぶすと言う言葉を使っています)
>>> sum = 1 + 2 + 3
>>> sum([1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
まあこの例のようなコード書く人はいないと思いますが(笑)、言いたいことはPythonのビルトイン関数ってよく使う変数名が結構多いということです。idとかlenとかmaxとか。
以降、思いつき(半分ネタ)も含めてこれにどう対応していけばいいかを説明します。
変数名の前か後ろに_を付ける
敬虔なPythonistaの皆さまであれば毎日三回ビルトイン関数名を暗唱されていることと思います。そう、ビルトイン関数と同じ変数名を使うなどそもそもありえないということです。
というのは冗談、でもって私は敬虔なPythonistaじゃないので全ビルトイン関数の名前を暗記はしてません。ただ、Pycharmなどの開発環境を使ってると「お前これビルトイン関数隠してるじゃねえかぼけー」と言ってくれます。そういう場合は前か後ろに_を付ければビルトイン関数をつぶすことありません。
>>> sum_ = 1 + 2 + 3
でもはっきり言ってダサい。
気にしない
私が主にやっている(?)方法です。つぶしても気にしません。
もう少しちゃんと説明するとつぶすと言ってもせいぜいその関数内、広くてそのモジュール(ファイル)内です。一つの関数で、ある名前をいわゆる変数として使いたいが同名のビルトイン関数も呼び出したいという状況はまずないでしょう。ていうかその場合、普通は変数の名前を変えます(「はじめに」でお見せしたようなボケを除く)
というわけで私は気軽にビルトイン関数と同じ名前を使ってます。
要らなくなったらdelする
delすると変数名がローカル変数一覧やグローバル変数一覧から取り除かれます1。そうすると名前検索の際につぶされてたビルトイン関数まで行くようになります。つまりこういうことです。
>>> sum = 1 + 2 + 3
>>> sum([1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>> del sum
>>> sum([1, 2, 3])
6
__builtins__を使う
ビルトイン関数はビルトインモジュールに定義された関数です。
変数一覧には初めから__builtins__というものが定義されており、このモジュールを明示的に指定することでつぶした後もビルトイン関数が使えます。
>>> sum = 1 + 2 + 3
>>> __builtins__.sum([1,2,3])
6
まとめ
というわけで変数名とビルトイン関数の関係、同じ名前の変数を使いたいときの対策を紹介してきました。方法としては以下のいずれかになります。
- 前か後ろに_をつける
- 気にしない
- 要らなくなったらdelする
- __builtins__を使う
余談
「つぶすと言ってもせいぜいそのファイル内だけだ、気にするな」と書きましたがビルトインモジュールも含めて考えてみると以下のようなことができるなと思いました。
import builtins
def fake_sum(*args):
print('Hello!')
builtins.sum = fake_sum
>>> import override_builtins
>>> sum([1,2,3])
Hello!
できるというだけでこれを使うとビルトイン関数上書きできるぜ!と推奨しているわけではまったくありません。敬虔なPythonistaではありませんが完全に邪道です。
ちなみにコンソールでは__builtins__に代入できるのにファイルに書くとエラーになるんですね。なんかの実行方法の違いだとは思いますが。
-
念のため。両方一気に取り除かれるわけではありません。そのスコープの一番内側にある変数名が取り除かれるという意味です ↩