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

sea*_*ans 11 python

我一直在阅读python中的魔术方法,我发现了很多关于覆盖它们以及它们服务的目的的信息,但我无法找到语言特定的操作符和操作映射到那些的位置方法(+寻找__add__,+=寻找__iadd__,创建从一个类的新对象可能调用__new____init__等)有没有什么地方我可以看到在Python解释器(或任何下级机构)遇到一个加号,会发生什么?

Bak*_*riu 5

您的问题有点笼统。尽管错过了一些stdlib特定的方法(例如,并由etc 使用,但是它是模块的协议而不是语言协议),但“特殊方法”的列表很全面。__setstate____getstate__picklepickle

如果您想确切地了解解释器的功能,则可以使用该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   
Run Code Online (Sandbox Code Playgroud)

您可以看到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;
Run Code Online (Sandbox Code Playgroud)

因此,在这里我们可以看到python特殊情况下的int和字符串加法PyNumber_Add运算,并最终返回到,检查第一个操作数是否实现__add__并调用它,最终它尝试__radd__在右侧进行操作,如果没有任何效果,则抛出a 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  
Run Code Online (Sandbox Code Playgroud)

同样,在将来的版本中也可以优化相同的字节码,因此即使字节码相同,不同版本的python实际上也会执行不同的指令。

如果您想了解python在后台的工作方式,我建议您按照可在python官方网站上找到的教程和文档编写一些C扩展。