問
次のコードを実行すると何が起きるだろうか。
想定はPython 3.6。Python3なら挙動は同じと予想される。
# Python 3系
import traceback
org_print = print
def dontdothat(*args, **kwargs):
raise RuntimeError("Don't do that")
try:
print = dontdothat
print('1')
except:
org_print(traceback.format_exc())
try:
del print
print('2')
except:
org_print(traceback.format_exc())
try:
del print
print('2')
except:
org_print(traceback.format_exc())
回答は「解説」の後にある
解説
デバッグ中、広範な範囲で print()
を使ってしまってそれを消したいので、それを無理やりランタイムエラーにしたい……なんていうことは、稀にはあるかもしれないとわずかながらに予想される。素直に ag
とかで探せよと思うけど。
そういうときには print
を上書きしてしまえば良い。ちなみに関数として上書きする必要すらなく、
try:
print = None
print('1')
except:
org_print(traceback.format_exc())
でも良い。 関数と変数でラベル付けの仕方 (def
と =
) が異なるが、Pythonランタイムは背後で同じ仕組みで管理する。言い換えると def func()...
と func = lambda ...
は内部的には大差ないらしい (逆に何が違うのと言われてもわからん)。
この場合 del print
はグローバル名前空間上にあるprint
とNone
の関連付け消す。その下からビルトイン名前空間の本来の print()
の実装がひょっこり頭を出す。
怖くない?
君も名前空間について勉強不足でPythonを使い続けているふれn
Pythonの名前空間を大まかに分けると「ビルトイン名前空間」「グローバル名前空間」「ローカル名前空間」になる。「ローカル名前空間」に属する名前空間は複数あり得る。関数の中に関数を定義して、さらにその中に関数を定義したりするケースが分かりやすい
a = 1 # グローバル名前空間
def func_in_global():
a = 2 # ローカル名前空間
def func_in_local():
a = 3 # ローカル名前空間 (2)
def func_in_local2():
a = 4 # ローカル名前空間 (3)
print(a)
print(a)
func_in_local2()
print(a)
print(a)
func_in_local()
print(a)
print(a)
func_in_global()
print(a)
さて、元々の print()
は「ビルトイン」に属し、del
では消せない。よって、冒頭のプログラムにおける3つ目の try〜except
ブロックにおいては、そのせいで例外を送出する。「そんな名前ないんですけど〜」というちょっと直感に反するメッセージが出ることに注意しよう。
名前の削除は、ローカルまたはグローバル名前空間からその名前の束縛を取り除きます。
いくら上書きして del
してもこの方法でビルトイン名前空間の print()
はいなくならない。
実行中のPythonランタイムから「本当に」組み込み関数 print()
を葬り去りたいのなら、多分こうするんだと思う。
import builtins
del builtins.__dict__['print']
print('hello')
ただ、これはヤバいので止めよう。
>>> len(builtins.__dict__)
152
>>> builtins.__dict__.clear()
>>> len(builtins.__dict__)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'len' is not defined
>>> builtins.__dict__.__len__()
1
>>> builtins.__dict__
{'_': None}
'_' 残像にアンダーバーを
回答
$ python /tmp/suppress_print.py
Traceback (most recent call last):
File "/tmp/suppress_print.py", line 9, in <module>
print('1')
File "/tmp/suppress_print.py", line 5, in dontdothat
raise RuntimeError("Don't do that")
RuntimeError: Don't do that
2
Traceback (most recent call last):
File "/tmp/suppress_print.py", line 21, in <module>
del print
NameError: name 'print' is not defined