让我们考虑这种具体情况,在这种情况下,我想传递一组某些对象的状态。为了方便和灵活(或任意),我选择使用二进制状态,然后将这些状态使用按位或“ |”连接在我绕过它们之前:
status_a | status_c == status_a + status_c
# 0b101 == 0b101 --> True
然后我意识到在这种情况下,我也可以使用加法算术运算符“ +”:
status_a | status_c | status_c == status_a + status_c + status_c
# 0b101 == 0b1001 --> False
当然,当状态为2的正幂时,这是正确的。还有其他一些警告,例如:
{{1}}
但是让我们假设我处于限制范围之内-是否有任何理由为什么按位运算符会比算术运算符更好? Python背后有东西吗?哪一个更快?也许还有其他我没有想到的副作用?
答案 0 :(得分:4)
当我们进一步查看Python源代码时,我们注意到运算符调用了不同的函数。加法运算符调用binary_op1()
,而OR运算符调用binary_op()
。
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.c
中的CPython project on GitHub。
答案 1 :(得分:2)
从Python 3.6开始,您要使用Flag枚举:
from enum import Flag, auto
class Status(Flag):
R = auto()
W = auto()
X = auto()
FULL = R | W | X
READONLY = R | X
a = Status.R
b = Status.W
c = Status.X
print(list(Status))
print(a,b,c)
perms1 = a | b | c
print(perms1)
# removing W permission
readonly_perms = perms1 & ~Status.W
print(readonly_perms == Status.READONLY)
print(Status.W in readonly_perms)
full_perms = readonly_perms | Status.FULL
print(full_perms == Status.FULL)
输出:
[<Status.R: 1>, <Status.W: 2>, <Status.X: 4>, <Status.FULL: 7>, <Status.READONLY: 5>]
Status.R Status.W Status.X
Status.FULL
True
True
True
检查Flag枚举文档here
不对位操作数和位掩码使用非按位运算的一个很好的理由是,您可以轻松地更改其他位作为副作用,而无需注意。
答案 2 :(得分:1)
使用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
答案 3 :(得分:1)
我一直在进行按位加法运算,以确定是否存在性能差异:
1)0-10的2的随机幂
# number of operations btw 1-100
import pandas as pd
import timeit
import numpy as np
from random import choice
scale = 100
df = pd.DataFrame({'num_operations' : np.arange(1, scale + 1), 'random_power_0_10': [[choice(range(10)) for _ in range(num_op)] for num_op in np.arange(1, scale + 1) ]})
df.head()
df['bitwise_timing'] = [timeit.timeit(stmt='reduce(lambda x, y: x | y, num)',
setup=f'from functools import reduce;num={num}')
for num in ([2**e for e in pows] for pows in df.random_power_0_10)]
df['addition_timing'] = [timeit.timeit(stmt='reduce(lambda x, y: x + y, num)',
setup=f'from functools import reduce;num={num}')
for num in ([2**e for e in pows] for pows in df.random_power_0_10)]
让我们绘制结果以查看差异
ax = df.set_index('num_operations').plot(grid=True, title='Bitwise vs addition operation for random powers(0-10) of 2')
ax.set_ylabel('time in seconds')
df.describe()
平均加法时间似乎更好,但是由于差异很小,我们可以说加法和按位运算之间没有差异
2)10-100的2的随机次幂
得到以下图:
在这种情况下,我们可以说加法运算更好