对于Python中的2的正幂,按位或“|”与加“+”相加

问题描述 投票:3回答:1

让我们考虑一下这种特殊情况,我希望传递一组对象的状态。为了方便和灵活(或者可能是任意),我选择使用二进制状态,然后使用按位或“|”连接在我传递它们之前:

status_a = 0b1
status_b = 0b10
status_c = 0b100

statuses_to_pass = status_a | status_c  # 0b101

然后我意识到在这种情况下我也可以使用加法算术运算符“+”:

status_a | status_c == status_a + status_c  
#             0b101 == 0b101  -->  True

当然,当状态是2的正幂时,这是正确的;还有其他一些警告,如:

status_a | status_c | status_c == status_a + status_c + status_c  
#                        0b101 == 0b1001  -->  False

但是我们假设我保持在限制范围内 - 是否有任何理由为什么按位运算符会优于算术运算符?在Python的引擎盖下有什么东西?哪一个更快?或者也许还有其他任何我没想到的副作用?

python bitwise-operators
1个回答
4
投票

使用timeit进行的实验表明,在这些情况下添加速度更快:

import timeit
import statistics
times = {"+": [], "|": []}

for x in range(10):
    for y in range(x+1, 10):
        for op in "+|":
            t = timeit.timeit(stmt="x {} y".format(op), setup="x=2**{};y=2**{}".format(x, y))
            times[op].append(t)


statistics.mean(times["+"])  # 0.029464346377385986
statistics.mean(times["|"])  # 0.04432822428643703

1
投票

当我们进一步研究Python源代码时,我们注意到运算符调用了不同的函数。加法运算符调用binary_op(),OR运算符调用binary_op1()

Python加法运算符(第955行)

PyObject *
PyNumber_Add(PyObject *v, PyObject *w)
{
    PyObject *result = binary_op1(v, w, NB_SLOT(nb_add));
    if (result == Py_NotImplemented) {
        PySequenceMethods *m = v->ob_type->tp_as_sequence;
        Py_DECREF(result);
        if (m && m->sq_concat) {
            return (*m->sq_concat)(v, w);
        }
        result = binop_type_error(v, w, "+");
    }
    return result;
}

Python OR运算符(第941行)

#define BINARY_FUNC(func, op, op_name) \
    PyObject * \
    func(PyObject *v, PyObject *w) { \
        return binary_op(v, w, NB_SLOT(op), op_name); \
    }

BINARY_FUNC(PyNumber_Or, nb_or, "|")

我们可能认为OR运算符比加法运算符更快,但OR运算符有更多的代码要执行。在Python中,OR运算符较慢,因为binary_op()调用binary_op1()

binary_op(第834行)

static PyObject *
binary_op(PyObject *v, PyObject *w, const int op_slot, const char *op_name)
{
    PyObject *result = binary_op1(v, w, op_slot);
    if (result == Py_NotImplemented) {
        Py_DECREF(result);

        if (op_slot == NB_SLOT(nb_rshift) &&
            PyCFunction_Check(v) &&
            strcmp(((PyCFunctionObject *)v)->m_ml->ml_name, "print") == 0)
        {
            PyErr_Format(PyExc_TypeError,
                "unsupported operand type(s) for %.100s: "
                "'%.100s' and '%.100s'. Did you mean \"print(<message>, "
                "file=<output_stream>)\"?",
                op_name,
                v->ob_type->tp_name,
                w->ob_type->tp_name);
            return NULL;
        }

        return binop_type_error(v, w, op_name);
    }
    return result;
}

binary_op1(第785行)

static PyObject *
binary_op1(PyObject *v, PyObject *w, const int op_slot)
{
    PyObject *x;
    binaryfunc slotv = NULL;
    binaryfunc slotw = NULL;

    if (v->ob_type->tp_as_number != NULL)
        slotv = NB_BINOP(v->ob_type->tp_as_number, op_slot);
    if (w->ob_type != v->ob_type &&
        w->ob_type->tp_as_number != NULL) {
        slotw = NB_BINOP(w->ob_type->tp_as_number, op_slot);
        if (slotw == slotv)
            slotw = NULL;
    }
    if (slotv) {
        if (slotw && PyType_IsSubtype(w->ob_type, v->ob_type)) {
            x = slotw(v, w);
            if (x != Py_NotImplemented)
                return x;
            Py_DECREF(x); /* can't do it */
            slotw = NULL;
        }
        x = slotv(v, w);
        if (x != Py_NotImplemented)
            return x;
        Py_DECREF(x); /* can't do it */
    }
    if (slotw) {
        x = slotw(v, w);
        if (x != Py_NotImplemented)
            return x;
        Py_DECREF(x); /* can't do it */
    }
    Py_RETURN_NOTIMPLEMENTED;
}

这些片段属于abstract.cCPython project on GitHub

© www.soinside.com 2019 - 2024. All rights reserved.