Python:高效的多字符串替换

Python:高效的多字符串替换,python,replace,soundex,Python,Replace,Soundex,这项功能能否更有效?我需要处理一百万个名字 def indian_soundex_encode(s): s = s.replace("aa", "a") s = s.replace("ee", "i") s = s.replace("zh", "l") s = s.replace("oo", "u") s = s.replace("bu", "b") s = s.replace("dh", "d") s = s.replace("gh",

这项功能能否更有效?我需要处理一百万个名字

def indian_soundex_encode(s):
    s = s.replace("aa", "a")
    s = s.replace("ee", "i")
    s = s.replace("zh", "l")
    s = s.replace("oo", "u")
    s = s.replace("bu", "b")
    s = s.replace("dh", "d")
    s = s.replace("gh", "g")
    s = s.replace("jh", "j")
    s = s.replace("kh", "k")
    s = s.replace("sh", "s")
    s = s.replace("th", "t")
    s = s.replace("ck", "k")
    s = s.replace("kk", "k")
    s = s.replace("nn", "n")
    s = s.replace("mm", "m")
    s = s.replace("pp", "p")
    s = s.replace("ll", "l")
    s = s.replace("ty", "ti")
    s = s.replace("ot", "od")
    s = s.replace("iya", "ia")
    s = s.replace("ya", "ia")
    s = s.replace("sv", "s")
    s = s.replace("sw", "s")
    s = s.replace("my", "mi")
    return s

使用纯Python很难提高函数的效率
str.replace
已经相当有效,但它确实需要多次扫描字符串,至少在某些情况下,需要创建几个新字符串。用只扫描字符串一次的更智能算法替换对
replace
的多个调用可能会使函数速度变慢,因为您将在纯Python中做更多的工作,并放弃
str.replace
的原始效率

如果在您的情况下可以编写C扩展模块,我建议您这样做。使用
timeit
测量,对于示例字符串
“foobaar”
,以下函数的性能比原始函数高出约17倍(0.184 usec,而Python版本为3.28 usec)

通过使用
switch
语句或更高效的用C编码的查找表,可以进一步加快上述函数的速度,但这将留给读者作为练习

另一个有趣的练习是尝试用Cython编写此函数的一个版本,并将其性能与上面手工编写的C扩展进行比较


更新:上述C函数对应于问题中的原始Python代码。编辑器Jost在格式更改的同时潜入了一个主要的代码更改,这显然没有被审阅者发现。

使用纯Python很难提高函数的效率
str.replace
已经相当有效,但它确实需要多次扫描字符串,至少在某些情况下,需要创建几个新字符串。用只扫描字符串一次的更智能算法替换对
replace
的多个调用可能会使函数速度变慢,因为您将在纯Python中做更多的工作,并放弃
str.replace
的原始效率

如果在您的情况下可以编写C扩展模块,我建议您这样做。使用
timeit
测量,对于示例字符串
“foobaar”
,以下函数的性能比原始函数高出约17倍(0.184 usec,而Python版本为3.28 usec)

通过使用
switch
语句或更高效的用C编码的查找表,可以进一步加快上述函数的速度,但这将留给读者作为练习

另一个有趣的练习是尝试用Cython编写此函数的一个版本,并将其性能与上面手工编写的C扩展进行比较


更新:上述C函数对应于问题中的原始Python代码。编辑Jost偷偷地进行了一次重大的代码更改和格式更改,这显然没有被审阅者发现。

这里有一个类似的问题,它的答案是:在我的计算机上,将函数应用于1000000个字符串需要4秒钟。对于您的用例来说,这真的太慢了吗?这不是前面问题的重复!这张海报特别要求效率,前一张海报要求代码简洁/优雅。长此以往,情况就不一样了。想想看:名字大多很短,而且只包含其中的一小部分,所以在24个名字中,只有一小部分会做任何事情,其余的只是扫描字符串。我知道的最长的印度名字是“Govindaraju Venkataraman”,它与上面的任何一个都不匹配(可能它已经通过了上面的编码?)。相关:这里你有一个类似的问题,它的答案是:在我的计算机上,将你的函数应用于1000000个字符串需要4秒钟。对于您的用例来说,这真的太慢了吗?这不是前面问题的重复!这张海报特别要求效率,前一张海报要求代码简洁/优雅。长此以往,情况就不一样了。想想看:名字大多很短,而且只包含其中的一小部分,所以在24个名字中,只有一小部分会做任何事情,其余的只是扫描字符串。我知道的最长的印度名字是“Govindaraju Venkataraman”,它与上面的任何一个都不匹配(可能它已经通过了上面的编码?)。相关:
PyObject *
indian_soundex_encode(PyObject *ignore, PyObject *args)
{
  PyObject *py_s, *py_ret;
  bool replaced = false;
  if (!PyArg_ParseTuple(args, "S", &py_s))
    return NULL;

  const char *s = PyString_AS_STRING(py_s);
  Py_ssize_t len = PyString_GET_SIZE(py_s);
  char *ret = malloc(len + 1), *retptr = ret;
  if (!ret)
    return PyErr_NoMemory();

  while (len > 0) {
#define REPLACE(first, second, replacement)     \
    if (*s == first && *(s + 1) == second) {    \
      s += 2;                                   \
      len -= 2;                                 \
      *retptr++ = replacement;                  \
      replaced = true;                          \
      continue;                                 \
    }

    REPLACE('a', 'a', 'a');
    REPLACE('e', 'e', 'i');
    REPLACE('z', 'h', 'l');
    REPLACE('o', 'o', 'u');
    REPLACE('b', 'u', 'b');
    REPLACE('d', 'h', 'd');
    REPLACE('g', 'h', 'g');
    REPLACE('j', 'h', 'j');
    REPLACE('k', 'h', 'k');
    REPLACE('s', 'h', 's');
    REPLACE('t', 'h', 't');
    REPLACE('c', 'k', 'k');
    REPLACE('k', 'k', 'k');
    REPLACE('n', 'n', 'n');

#undef REPLACE
    *retptr++ = *s++;
    --len;
  }
  if (!replaced) {
    py_ret = py_s;
    Py_INCREF(py_ret);
  }
  else
    py_ret = PyString_FromStringAndSize(ret, retptr - ret);
  free(ret);
  return py_ret;
}