LoginSignup
0
0

More than 5 years have passed since last update.

del 一題

Last updated at Posted at 2017-02-26

次のコードを実行すると何が起きるだろうか。
想定は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 はグローバル名前空間上にあるprintNoneの関連付け消す。その下からビルトイン名前空間の本来の 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の仕様

名前の削除は、ローカルまたはグローバル名前空間からその名前の束縛を取り除きます。

いくら上書きして 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
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0