【Python】print文を改竄する方法
こんばんは。PHPを書くようになって半年が経ったのに、手に馴染まないのでフラストレーションが溜まっている小佐田です。
今回はPythonの組み込み関数とPythonの中でのその扱いについてです。
Pythonコーディングでは、組み込み関数を上書きすることができます。
実際そのようなコーディングはやるべきではありませんが、興味で挙動を調べてみました。
こういうのです。
print("hello world")
と書くと、
画面に出力できると思った〜? 残念でした🥺
なんて表示させる方法です。
繰り返しになりますが、最悪のコーディングなので遊び以外ではやらないでくださいね。
print文について
Pythonのprint文を使うと標準出力に文字を表示することができます。
print("Hello")
なんて書くと、画面上に`Hello`が表示されるといった具合です。Pythonの基礎も基礎なのでご存じの方も多いと思います。
Pythonの関数
Pythonの関数はどのような存在として定義されているのでしょうか。
`print`関数を用いて`print`を出力してみました。
print(print)
<built-in function print>
標準搭載されている関数だということがわかりますね。実はこれは`print`という変数の中に関数の存在が定義されていると解釈することができます。
代入する形式での関数定義
関数の存在を変数の中に入れられるということは次のような構文も成立するということです。
def test1():
print("hoge")
my_func = test1
my_func()
test1という何でもない関数を定義しました。単純に標準出力するだけですね。
それを`my_func`という変数に代入しました。つまり関数を変数に代入したということです。
ですので、`my_func()`と呼び出すと、test1の内容が呼ばれるということです。
余談: 関数の代入は参照渡しなのか
余談ですが、`my_func = test1`と書いたときに関数がコピーされているのか、関数までの参照がコピーされているのか疑問に思ったので試してみました。該当文を実行した後に同名の関数をオーバーライドしてみました。
def test1():
print("hoge")
my_func = test1
def test1():
print("hoge2")
my_func()
hoge
`my_func = test1`と書いた時に参照渡しではなく関数の内容がコピーされているようです。
余談2: ラムダ式がそれじゃないですかね……?
ラムダ式を学ぶと上記の内容がわかりやすいと思います。
無名関数を作成してそれを変数に代入する形ですね。
my_func = lambda text: f"textが関数に渡されました"
print(my_func("hello"))
textが関数に渡されました
ここからが本題 組み込み関数を書き換えよう!
実務ではやめてくださいね。同僚にやられたら怒りますよ。
標準関数も変数に対して定義されているようなものです。ですので、動的な型変換が行われ代入することができます。
def print(text):
pass
print("hello world")
(出力はありません)
はい。`print`関数が何もしない関数で書き換えられてしまいました。
ではこのprint関数を実行すると相手を煽ることができる文章を出力できるようにしてみましょう。
# range関数をprint関数で上書き
range = print
# print関数を上書き
def print(text):
range("画面に出力できると思った〜? 残念でした🥺")
# 実行
print("hello")
画面に出力できると思った〜? 残念でした🥺
これは標準出力の機能をrange関数(これも組み込み関数ですね)に代入しています。その後、print関数を違う文字列を出力する関数で上書きしています。ですので呼び出されたのは改竄された後のprint関数だということです。
最悪のモジュール: 組み込み関数を書き換えるモジュール
さて、ここまできたらやるところまでやってみましょう。モジュール自体に上のコードを書くことでモジュールをimportするだけで組み込み関数が書きかわるようにすることもできます。
`saiaku.py`を作りました。
range = print
def print(text: str):
"""標準出力に文字に出力します
Args:
text (str): 出力する文字列
"""
range("文字が表示できると思いましたか? 残念でした🥺")
で本体でこれをimportするのですが、これから全ての関数をimportするだけでprint文がオーバーライドされます。
from saiaku import *
print("hello")
文字が表示できると思いましたか? 残念でした🥺
教訓: 再代入には気をつけて
Pythonのような動的型付け言語はこのように違う型の値で上書きすることができてしまいます。
案外関数と変数は間違えることがあります。関数に対して代入すると別の関数でなくても変数などでオーバーライドすることがあります。予期せぬエラーが発生する可能性があるので、そのようなことをしないように常に型を意識してコーディングするのが必要なのだと思います。