我一直在阅读python中的魔术方法,我发现了很多关于覆盖它们以及它们服务的目的的信息,但是我无法找到特定于语言的操作符和操作的位置。映射到这些方法(+
查找__add__
,+=
查找__iadd__
,从类创建新对象可能会调用__new__
和{{1}在某些地方,我可以看到当python解释器(或任何低级机制)遇到加号时会发生什么?
答案 0 :(得分:5)
你的问题有点泛泛。有一个全面的list“特殊方法”,即使它错过了一些特定于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
您可以看到intereper在执行添加时执行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和字符串添加,并最终回退到PyNumber_Add
,它检查第一个操作数是否实现__add__
并调用它,最终它尝试{{1右侧的,如果没有任何效果,则会引发__radd__
。
请注意,字节代码是特定于版本的,因此TypeError
将在不同版本上显示不同的结果:
dis
同样的字节代码可能会在将来的版本中进行优化,所以即使字节代码相同,python的不同版本也会实际执行不同的指令。
如果您有兴趣了解python如何在幕后工作,我建议您编写一些C扩展,遵循官方python网站上的教程和文档。
答案 1 :(得分:4)
由于所涉及的抽象级别,精确定位CPython源中映射运算符+
到特殊方法__add__
的单个位置并非易事。
正如其他人回复的那样,+
是使用BINARY_ADD
操作码实现的,该操作码调用PyNumber_Add
(除了在一些特别优化的情况下)。另一方面,PyNumber_Add
查看tp_as_number
member的type object以获取PyNumberMethods
结构,其nb_add
成员指向实现的C函数此外。
对于直接define their own nb_add
的内置类型,这很简单,但对于在Python中定义的__add__
有点复杂,需要将其翻译为适当的nb_add
。此部分由typeobject.c
处理:当您定义实现__add__
的类时,typeobject.c
installs中的机制进入object->type->tp_as_number->nb_add
查找__add__
的通用函数对象上的{1}}并调用它来实现添加。对于__add__
的情况,此通用函数称为slot_nb_add
,使用defined为SLOT1BIN
macro。
至于__new__
和__init__
,它们在CPython实现术语中被调用from the __call__
operator of the type
object本身(tp_call
)。这是合乎逻辑的,因为在Python中,您调用的是构造对象的类型。
答案 2 :(得分:3)
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
答案 3 :(得分:2)
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
那就是你最接近的“低级别”
答案 4 :(得分:1)