【发布时间】:2013-02-26 14:06:46
【问题描述】:
我刚开始使用 Cython,结果也很难用谷歌搜索 Cython 特定的东西,所以提前抱歉。
我正在用 Cython 重新实现一个 Python 函数。它在 Python 中几乎是这样的:
def func(s, numbers=None):
if numbers:
some_dict = numbers
else:
some_dict = default
return sum(some_dict[c] for c in s)
它在 Python 2 和 3 上运行良好。但如果我尝试输入 s 和 c,它至少会在一个 Python 版本上中断。我试过了:
def func(char *s, numbers=None):
if numbers:
some_dict = numbers
else:
some_dict = default
cdef char c
cdef double m = 0.0
for c in s:
m += some_dict[<bytes>c]
return m
老实说,这是我唯一要做的事情,它在 Python 2 上提供了不错的加速,但在 Python 3 上中断了。阅读了 this 的 Cython 文档后,我认为以下内容会在 Python 3 上工作:
def func(unicode s, numbers=None):
if numbers:
some_dict = numbers
else:
some_dict = default
cdef double m = 0.0
for c in s:
m += some_dict[c]
return m
但它实际上引发了KeyError 并且似乎c 仍然是char(如果s 以'P' 开头,则缺少的键是80)但是当我print(type(c)) 它时说<class 'str'>。
请注意,原始无类型代码在这两个版本下都可以工作,但比 Python 2 上的工作类型版本慢大约两倍。
那么我如何让它在 Python 3 上运行,然后如何让它同时在两个 Python 版本上运行呢?我可以/应该在类型/版本检查中包装类型声明吗?或者我应该编写两个函数并有条件地将其中一个分配给一个公开可用的名称?
附:如果重要的话,我可以只允许字符串中的 ASCII 字符,但我怀疑它确实如此,因为 Cython 似乎更倾向于显式编码/解码。
编辑:我也尝试过显式编码和迭代字节串,这是有道理的,但是下面的代码:
def func(s, numbers=None):
if numbers:
some_dict = numbers
else:
some_dict = default
cdef double m = 0.0
cdef bytes bs = s.encode('ascii')
cdef char c
for c in bs:
m += some_dict[(<bytes>c).decode('ascii')]
return m
比我在 Python 2 上的第一次尝试慢 3 倍(接近纯 Python 函数的速度),在 Python 3 上慢了近 2 倍。
【问题讨论】:
-
do_stuff is python 函数中几乎没有加速。在这种情况下,您只是输入了循环变量,而不是工作。在 cython 中重写 do_stuff。如果您提供有关 do_stuff 的作用以及 some_dict 值中的内容的信息也会很有帮助。
-
关于您的 KeyError - 在 C unicode 中通常映射到 int 类型,因此在这种情况下 some_dict 必须是带有 int 键的 C 哈希(或者可能更正确的 Py_UNICODE 类型)。但同样,我敢打赌瓶颈在 do_stuff 中。
-
@TurnaevEvgeny
do_stuff是来自some_dict的数字的算术运算。基本上,它计算来自some_dict的值的总和,对应于来自s的键。我输入了 sum 变量,所以有一些加速。所以问题是如何对循环本身进行cythonize。 -
对我来说仍然很不清楚。使用示例数据发布更多代码。如果您要将值映射到 255 范围内的任何字符 - 那么只需使用数组而不是 dict。函数应该返回什么?
-
@TurnaevEvgeny 这不是任何字符,只是 ascii 大写的一个子集。但是仍然制作一个(稀疏)数组听起来像是一个理智的想法。返回的值是计算的总和。
标签: python c string unicode cython