Python中乘方运算符「**」的底层原理是什么?
起首查看下字节码
import dis def test(): a = 1 a = a ** 2 print(dis.dis(test))能够看到乘方运算的字节码为BINARY_POWER
然后查看CPython源码,发现,并没有那个字节码对应的代码,但是能够查到另一个相关的,在cpython/python/ceval.c 877行
static const binaryfunc binary_ops[] = { ... [NB_POWER] = _PyNumber_PowerNoMod, ... };在二元操做符中有一个名为NB_POWER的操做符,而且对应_PyNumber_PowerNoMod函数。现实上那个就是算乘方的函数
进一步查找_PyNumber_PowerNoMod,在cpython/Objects/abstract.c 1157行
PyObject * _PyNumber_PowerNoMod(PyObject *lhs, PyObject *rhs) { return PyNumber_Power(lhs, rhs, Py_None); }挪用了PyNumber_Power,那个函数在cpython/Objects/abstract.c 1152行
PyObject * PyNumber_Power(PyObject *v, PyObject *w, PyObject *z) { return ternary_op(v, w, z, NB_SLOT(nb_power), "** or pow()"); }挪用了ternary_op,那个函数在cpython/Objects/abstract.c 962行,函数声明如下
static PyObject * ternary_op(PyObject *v, PyObject *w, PyObject *z, const int op_slot, const char *op_name ) { ... }那个函数很复杂,因为CPython会尽可能计算成果。详细而言,关于传入的三个参数,会别离测验考试计算w.op(v,w)[*], v.op(v,w), w.op(v,w)。那个函数关键是挪用了slot,然后别离实现差别类型的计算
关于long类型,在longobject.c内,cpython/Objects/longobject.c,有名为long_pow的函数用于计算幂,最末定位到4499行的MULT
#define MULT(X, Y, result) { ... }会发现幂函数计算利用的办法为(本人并非搞那个的,貌似是一种滑动窗口法?)
/* Left-to-right binary exponentiation (HAC Algorithm 14.79) */ /* http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf */ /* Find the first significant exponent bit. Search right to left * because were primarily trying to cut overhead for small powers. */关于float类型,在floatobject.c内,有名为float_pow的函数用于计算幂。查看float_pow会发现多了各类判断。起首停止各类判断,制止出错,最初交由pow函数计算
static PyObject * float_pow(PyObject *v, PyObject *w, PyObject *z) { ... /* Sort out special cases here instead of relying on pow() */ if (iw == 0) { /* v**0 is 1, even 0**0 */ return PyFloat_FromDouble(1.0); } if (Py_IS_NAN(iv)) { /* nan**w = nan, unless w == 0 */ return PyFloat_FromDouble(iv); } if (Py_IS_NAN(iw)) { /* v**nan = nan, unless v == 1; 1**nan = 1 */ return PyFloat_FromDouble(iv == 1.0 ? 1.0 : iw); } if (Py_IS_INFINITY(iw)) { ... } if (Py_IS_INFINITY(iv)) { ... } if (iv == 0.0) { if (iw < 0.0) { ... } ... return PyFloat_FromDouble(iw_is_odd ? iv : 0.0); } if (iv < 0.0) { if (iw != floor(iw)) { return PyComplex_Type.tp_as_number->nb_power(v, w, z); } iv = -iv; negate_result = DOUBLE_IS_ODD_INTEGER(iw); } if (iv == 1.0) { return PyFloat_FromDouble(negate_result ? -1.0 : 1.0); } /* Now iv and iw are finite, iw is nonzero, and iv is * positive and not equal to 1.0. We finally allow * the platform pow to step in and do the rest. */ errno = 0; ix = pow(iv, iw); _Py_ADJUST_ERANGE1(ix); if (negate_result) ix = -ix; ... return PyFloat_FromDouble(ix); }
0