5

我刚从 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 上运行良好。但如果我尝试输入sand 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 上中断了。阅读了这段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

但它实际上引发了 aKeyError并且似乎c仍然是 a char(缺少的关键是80ifs以 a 开头'P')但是当我print(type(c))<class 'str'>.

请注意,原始的无类型代码在两个版本下都可以工作,但比 Python 2 上的工作类型版本慢大约两倍。

那么如何让它在 Python 3 上运行,然后如何让它同时在两个 Python 版本上运行呢?我可以/应该在类型/版本检查中包装类型声明吗?或者我是否应该编写两个函数并有条件地将其中一个分配给一个公开可用的名称?

PS 如果重要的话,我可以只允许字符串中的 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 倍。

4

1 回答 1

0

foo.h

// #include <unistd.h>;  // for ssize_t
double foo(char * str, ssize_t str_len, double weights[256]){
    double output = 0.0;
    int i;
    for(i = 0; i < str_len; ++i){
        output += weights[str[i]];
    }
    return output;
}

from cpython.string cimport PyString_GET_SIZE, PyString_Check, PyString_AS_STRING

cdef extern from "foo.h":
    double foo(char * str, ssize_t str_len, double weights[256])   

cdef class Numbers:
    cdef double nums[256]

    def __cinit__(self, py_numbers):
        for x in range(256):
            self.nums[i] = py_numbers[i]

def py_foo(my_str, Numbers nums_inst):
    cdef:
        double res
    # check here my_str is BYTEstring
    if not PyString_Check(my_str):
        raise TypeError("bytestring expected got %s instead" % type(my_str))
    res = foo(PyString_AS_STRING(my_str), PyString_GET_SIZE(my_str), nums_inst.nums)
    return res

(未经测试)

于 2013-03-12T12:51:58.450 回答