私はPythonのマジックメソッドについて読んでいて、それらをオーバーライドすることとそれらが何を提供するかについて多くの情報を見つけましたが、言語固有の演算子とアクションがそれらにマップされている場所を見つけることができませんでしたメソッド (+
を探す__add__
、 を+=
探す__iadd__
、クラスから新しいオブジェクトを作成すると、 __new__
and__init__
などが呼び出される可能性があります。Python インタープリター (または下位レベルのメカニズム) がプラス記号に遭遇したときに何が起こるかを確認できる場所はありますか?
5 に答える
あなたの質問は少し一般的です。「特別なメソッド」の包括的なリストがありますが、いくつかの stdlib 固有のメソッドが欠落しています (たとえば__setstate__
、など__getstate__
によって使用されます。ただし、これは言語プロトコルpickle
ではなくモジュールのプロトコルです)。pickle
インタープリターが何をするかを正確に知りたい場合は、dis
モジュールを使用してバイトコードを逆アセンブルできます。
>>> import dis
>>> def my_func(a):
... return a + 2
...
>>> dis.dis(my_func)
2 0 LOAD_FAST 0 (a)
3 LOAD_CONST 1 (2)
6 BINARY_ADD
7 RETURN_VALUE
BINARY_ADD
加算を行う際にインターリーパーがバイトコードを実行することがわかります。実行する操作を正確に確認したい場合は、BINARY_ADD
Python のソース コードをダウンロードしてceval.c
ファイルを確認できます。
case BINARY_ADD:
w = POP();
v = TOP();
if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
/* INLINE: int + int */
register long a, b, i;
a = PyInt_AS_LONG(v);
b = PyInt_AS_LONG(w);
/* cast to avoid undefined behaviour
on overflow */
i = (long)((unsigned long)a + b);
if ((i^a) < 0 && (i^b) < 0)
goto slow_add;
x = PyInt_FromLong(i);
}
else if (PyString_CheckExact(v) &&
PyString_CheckExact(w)) {
x = string_concatenate(v, w, f, next_instr);
/* string_concatenate consumed the ref to v */
goto skip_decref_vx;
}
else {
slow_add:
x = PyNumber_Add(v, w);
}
Py_DECREF(v);
skip_decref_vx:
Py_DECREF(w);
SET_TOP(x);
if (x != NULL) continue;
break;
したがって、ここでは、Python の特殊なケースで int と string の追加が行われ、最終的に にフォールバックしPyNumber_Add
、最初のオペランドがそれを実装__add__
して呼び出すかどうかを確認し、最終的に__radd__
右側を試し、何も機能しない場合は を発生させTypeError
ます。
バイト コードはバージョン固有であるためdis
、異なるバージョンでは異なる結果が表示されることに注意してください。
# python2.7
>>> def my_func():
... return map((lambda x: x+1), range(5))
...
>>> dis.dis(my_func)
2 0 LOAD_GLOBAL 0 (map)
3 LOAD_CONST 1 (<code object <lambda> at 0x16f8c30, file "<stdin>", line 2>)
6 MAKE_FUNCTION 0
9 LOAD_GLOBAL 1 (range)
12 LOAD_CONST 2 (5)
15 CALL_FUNCTION 1
18 CALL_FUNCTION 2
21 RETURN_VALUE
# python3
>>> dis.dis(my_func)
2 0 LOAD_GLOBAL 0 (map)
3 LOAD_CONST 1 (<code object <lambda> at 0x7f1161a76930, file "<stdin>", line 2>)
6 LOAD_CONST 2 ('my_func.<locals>.<lambda>')
9 MAKE_FUNCTION 0
12 LOAD_GLOBAL 1 (range)
15 LOAD_CONST 3 (5)
18 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
21 CALL_FUNCTION 2 (2 positional, 0 keyword pair)
24 RETURN_VALUE
また、将来のバージョンでは同じバイトコードが最適化される可能性があるため、バイトコードが同じであっても、異なるバージョンの python では実際には異なる命令が実行されます。
Python が舞台裏でどのように機能するかを知りたい場合は、Python の公式 Web サイトにあるチュートリアルとドキュメントに従って、C 拡張機能を作成することをお勧めします。
関連する抽象化のレベルのため、演算子+
を特殊なメソッドにマッピングする CPython ソースの単一の場所を特定することは自明ではありません。__add__
他の応答として、を呼び出すオペコードで+
実装されます(特別に最適化された場合を除く)。一方、型オブジェクトのメンバーを調べて、メンバーが加算を実装する C 関数を指す構造体に到達します。BINARY_ADD
PyNumber_Add
PyNumber_Add
tp_as_number
PyNumberMethods
nb_add
これは、独自の型を直接定義する 組み込み型の場合は簡単ですが、適切な. この部分は によって処理されます。 を実装するクラスを定義すると、 の機構が汎用関数に組み込まれます。この関数はオブジェクトを検索し、それを呼び出して追加を実装します。の場合、この汎用関数が呼び出され、マクロを使用して定義されます。 nb_add
__add__
nb_add
typeobject.c
__add__
typeobject.c
object->type->tp_as_number->nb_add
__add__
__add__
slot_nb_add
SLOT1BIN
__new__
およびに関しては__init__
、それらはオブジェクト自体のオペレーターから__call__
type
tp_call
呼び出されます ( CPython 実装用語で)。Python では型を呼び出してオブジェクトを構築するため、これは論理的です。
dis
モジュールはこれでいくらかあなたを助けることができます:
単純なリストの例を見てみましょう:
In [12]: def func():
lis=[1,2,3]
for i in range(5):
lis+=[i]
....:
In [13]: def func1():
lis=[1,2,3]
for i in range(5):
lis =lis + [i]
....:
In [14]: dis.dis(func)
2 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
#removed some lines of code
4 34 LOAD_FAST 0 (lis)
37 LOAD_FAST 1 (i)
40 BUILD_LIST 1
43 INPLACE_ADD # += means inplace add is used
# i.e `__iadd()__`
44 STORE_FAST 0 (lis)
47 JUMP_ABSOLUTE 28
>> 50 POP_BLOCK
>> 51 LOAD_CONST 0 (None)
54 RETURN_VALUE
In [15]: dis.dis(func1)
2 0 LOAD_CONST 1 (1)
3 LOAD_CONST 2 (2)
6 LOAD_CONST 3 (3)
9 BUILD_LIST 3
12 STORE_FAST 0 (lis)
#removed some lines of code
4 34 LOAD_FAST 0 (lis)
37 LOAD_FAST 1 (i)
40 BUILD_LIST 1
43 BINARY_ADD #normal binary add was used
#i.e __add__
44 STORE_FAST 0 (lis)
47 JUMP_ABSOLUTE 28
>> 50 POP_BLOCK
>> 51 LOAD_CONST 0 (None)
54 RETURN_VALUE
http://docs.python.org/2/library/dis.html
class x:
def __add__(self,other):
return "asd"
def test():
return x() + "aaaa"
import dis
dis.dis(test)
次のようなものを返します
2 0 LOAD_GLOBAL 0 (x)
3 CALL_FUNCTION 0
6 LOAD_CONST 1 ('aaaa')
9 BINARY_ADD
10 RETURN_VALUE
それが「低レベル」に最も近いものです
ドキュメントのこの部分を確認することをお勧めします。
http://docs.python.org/3/reference/datamodel.html#special-method-names