运算符在python中映射到魔术方法的位置?

时间:2012-11-11 18:42:18

标签: python

我一直在阅读python中的魔术方法,我发现了很多关于覆盖它们以及它们服务的目的的信息,但是我无法找到特定于语言的操作符和操作的位置。映射到这些方法(+查找__add__+=查找__iadd__,从类创建新对象可能会调用__new__和{{1}在某些地方,我可以看到当python解释器(或任何低级机制)遇到加号时会发生什么?

5 个答案:

答案 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 membertype 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,使用definedSLOT1BIN 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)

您可能需要查看文档的这一部分:

http://docs.python.org/3/reference/datamodel.html#special-method-names