Python 3中字符串的三向比较

Python 3中字符串的三向比较,python,string,python-3.x,Python,String,Python 3.x,假设您想要优化用Python实现的(字节)字符串比较密集型算法。因为中心代码路径包含这一系列语句 if s < t: # less than ... elif t < s: # greater than ... else: # equal ... 如果sa))。这将为您提供r,但是。。。这仍然是两个比较。我尝试了一个基于python的bytes\u compare实现。。。非常糟糕的主意:)至少3倍的速度注:为了使这一点在几乎零开销的情况下更易于重用,用更通

假设您想要优化用Python实现的(字节)字符串比较密集型算法。因为中心代码路径包含这一系列语句

if s < t:
    # less than ...
elif t < s:
    # greater than ...
else:
    # equal ...
如果s
它将是伟大的,以优化它的东西,如

r = bytes_compare(s, t)
if r < 0:
    # less than ...
elif r > 0:
    # greater than ...
else:
    # equal ...
r=bytes\u比较(s,t)
如果r<0:
#少于。。。
如果r>0:
#大于。。。
其他:
#相等的。。。
其中(假设的)
bytes\u compare()
理想情况下只调用C函数,该函数通常经过了很好的优化。这将使字符串比较的数量减少一半。除非字符串超短,否则这是一个非常可行的优化

但是如何使用Python 3实现这一目标呢

PS

Python3删除了三向比较全局函数
cmp()
和神奇的方法
\uuu cmp\uu()
。即使使用Python2,
bytes
类也没有
\uuu cmp\uuu()
成员

使用
ctypes
包可以直接调用
memcmp()
,但是
ctypes
的外部函数调用开销非常高。

Python3(包括3.6)根本不包括对字符串的任何三向比较支持。尽管富比较运算符
的内部实现确实调用了
memcmp()
(在
bytes
-cf.
对象/bytesobject.C
的C实现中)但没有可以利用的内部三向比较函数

因此,通过调用
memcmp()
编写一个提供三向比较函数的

#include <Python.h>
static PyObject* cmp(PyObject* self, PyObject* args) {
    PyObject *a = 0, *b = 0;
    if (!PyArg_UnpackTuple(args, "cmp", 2, 2, &a, &b))
        return 0;
    if (!PyBytes_Check(a) || !PyBytes_Check(b)) {
        PyErr_SetString(PyExc_TypeError, "only bytes() strings supported");
        return 0;
    }
    Py_ssize_t n = PyBytes_GET_SIZE(a), m = PyBytes_GET_SIZE(b);
    char *s = PyBytes_AsString(a), *t = PyBytes_AsString(b);
    int r = 0;
    if (n == m) {
        r = memcmp(s, t, n);
    } else if (n < m) {
        r = memcmp(s, t, n);
        if (!r)
            r = -1;
    } else {
        r = memcmp(s, t, m);
        if (!r)
            r = 1;
    }
    return PyLong_FromLong(r);
}
static PyMethodDef bytes_util_methods[] = {
    { "cmp", cmp, METH_VARARGS, "Three way compare 2 bytes() objects." },
    {0,0,0,0} };
static struct PyModuleDef bytes_util_def = {
    PyModuleDef_HEAD_INIT, "bytes_util", "Three way comparison for strings.",
    -1, bytes_util_methods };
PyMODINIT_FUNC PyInit_bytes_util(void) {
    Py_Initialize();
    return PyModule_Create(&bytes_util_def);
}
测试:


与通过
ctypes
包调用
memcmp
不同,这个外部调用与内置字节比较运算符具有相同的开销(因为它们在标准Python版本中也是作为C扩展实现的)。

听起来像是在Python的循环中进行字符串比较。这将永远是缓慢的。您最好使用Cython、Pandas等相关工具设计一个合适的快速解决方案。但这样做需要对周围的代码进行全面检查,而不是对单个字符串进行微基准比较。比较是如何实现的,鉴于
s
t
字节
?@JohnZwinck,我用pyflame做了一些分析,这验证了在这些成对比较中花费了大量时间(
s
t
之后是
t
s
)。请注意,这种模式并不罕见——例如,当您合并/连接2个排序的字符串序列时,也会出现这种情况。此外,Python字符串比较通常并不慢——如果您只需要对
s
t
进行一次比较,那么它们的速度也很快。因此,这并不是孤立的微基准标记。@urban,在Python3.6.5中,有
对象/bytesobject.c
,它在
bytes\u compare\u eq()
bytes\u richcompare()
(对于
)中调用
memcmp()
。在Python3中讨论
cmp()
,建议的方法是:
((a>b)-(b>a))
。这将为您提供
r
,但是。。。这仍然是两个比较。我尝试了一个基于python的
bytes\u compare
实现。。。非常糟糕的主意:)至少3倍的速度注:为了使这一点在几乎零开销的情况下更易于重用,用更通用的缓冲区接口(带有
标志
)取代
PyBytes
API的显式使用将允许它与
bytearray
mmap
)无缝工作,
array.array('B')
,以及所有其他类似
字节的对象。您只需在堆栈上声明一对
Py\u buffer
结构,用w/
PyObject\u GetBuffer
和您的输入填充它们,然后在当前使用
s/t
m/n
的位置使用
buf.buf
buf.len
gcc -Wall -O3 -fPIC -shared bytes_util.c -o bytes_util.so -I/usr/include/python3.6m
>>> import bytes_util
>>> bytes_util.cmp(b'foo', b'barx')
265725