-
需要があるのか無いのかわかりませんが、ずーっと薄ぼんやり「なんでこれちゃんと動くんや・・・」って思ってたことのメモになります
-
誰かのお役に立てれば幸いです
備考
- このコンテンツは生成AIの出力も含まれています
Pythonの謎挙動について
- C++でインスタンスメソッドを関数ポインタとして渡す時にクラスがないと渡せなかった。
- Pythonだとインスタンスメソッドをそのまま渡せることが多いなと思っていました。
Pythonの束縛メソッドについて
- Pythonでは、クラスのインスタンスメソッドは、第一引数として自動的にそのインスタンス自身(self)を受け取ります。これを 束縛メソッド(bound method) と呼ぶそうです。
- イメージとしてはインスタンスメソッドがインスタンスの参照を保持しており、呼び出される時にselfとして受け取ることが出来ると、解釈して良さそう
Pythonの「束縛メソッド」の仕組み(イメージ)
インスタンスの生成
- generator = PromptGenerator() のようにインスタンスが作られると、メモリ上にgeneratorオブジェクトが確保されます。
メソッドへのアクセス
- generator.metric のように、インスタンス経由でメソッドにアクセスした瞬間に、Pythonは裏で特別なオブジェクトをその場で生成します。
- これが束縛メソッド (bound method) オブジェクトです。
束縛メソッドが保持しているもの
この束縛メソッドオブジェクトは、内部に2つの重要な情報を「ペアで」保持している。
self 属性: 元になったインスタンスへの参照(この例では generator オブジェクト)。
func 属性: クラスに定義されている、元のただの関数(この例では PromptGenerator クラス内の metric 関数)。
イメージ「インスタンスメソッドがインスタンスの参照を保持しており」というのは、この__self__属性のことを指している。
束縛メソッドの呼び出し
この束縛メソッドオブジェクトが () を付けて呼び出されると、Pythonは以下のように振る舞う。
「func(ただの関数)を呼び出すぞ。その際、第一引数には__self__(インスタンス参照)を自動的に渡し、残りの引数は呼び出し時に指定されたものをそのまま渡そう」
つまり、
method(gold, pred)
という呼び出しは、内部的に
function(instance, gold, pred)
という形で実行されます。
(metric(self, gold, pred))
C++との対比
C++のメンバ関数ポインタ
C++では「クラスのどのメンバ関数か」という関数の種類(オフセットのようなもの)だけを保持しています。そのため、呼び出す際には、どのインスタンス(thisポインタ)に対して実行するのかを別途明示的に指定する必要があります。
(instance->*member_function_pointer)(arg1, arg2);
Pythonの束縛メソッド
Pythonは「どのインスタンスの」「どの関数か」というペアの情報を関数オブジェクト自体が保持しています。そのため、一度このオブジェクトを取得してしまえば、あとは通常の関数と同じように、インスタンスを意識せずに呼び出すことができます。
参考サイト
Python公式ドキュメント - デスクリプタ ガイド (日本語)
URL: https://docs.python.org/ja/3/howto/descriptor.html
感想
- ほぼイメージ通り
- まぁちゃんと調べとけよなって思っていた部分ではある
- Pythonの方が使い勝手が良く、融通が効きやすい反面、遅そうだなーと思った
- パフォーマンスに影響が出るような呼び出し回数でなければ、Pythonの方が良いね