3

首先,我将 Cython 0.18 与 Python 2.7.4 一起使用。我遇到了一个相当奇怪的错误,我不知道为什么。这是玩具代码:

from cpython cimport bool

cpdef unsigned int func(char *seq1, char *seq2, bool case_sensitive=True):
        print 'seq1', seq1, len(seq1)
        print 'seq2', seq2, len(seq2)
        print

        #take care of case sensitivity
        if not case_sensitive:
                #this is kinda hacky, but I've gotta assign the lowercased string to a Python object before assigning it back to char *
                #see http://docs.cython.org/src/userguide/language_basics.html#caveats-when-using-a-python-string-in-a-c-context
                temp = seq1.lower()
                seq1 = temp

                temp = seq2.lower()
                seq2 = temp

        print 'seq1', seq1, len(seq1)
        print 'seq2', seq2, len(seq2)
        print

        #trim common characters at the beginning of the words
        while len(seq1) > 0 and len(seq2) > 0 and seq1[0] == seq2[0]:
                temp = seq1[1:]
                seq1 = temp

                temp = seq2[1:]
                seq2 = temp

        print 'seq1', seq1, len(seq1)
        print 'seq2', seq2, len(seq2)
        print

        #handle degenerate cases
        if not seq1:
                return len(seq2)
        if not seq2:
                return len(seq1)

这是一个示例调用:

>>> from func import func
>>> print func('TUESDAYs', 'tuesday', False)

现在,我希望看到以下内容:

seq1 TUESDAYs 8
seq2 tuesday 7

seq1 tuesdays 8
seq2 tuesday 7

seq1 s 1
seq2  0

1

但我实际看到的是这样的:

seq1 TUESDAYs 8
seq2 tuesday 7

seq1 tuesdays 8
seq2 tuesday 7

seq1 stdout 6
seq2 tuesday 7

0

这到底是怎么回事?首先,为什么要stdout输出?为什么我没有得到我应该得到的输出?这是 Cython 错误,还是我只是在这里遗漏了一些微不足道的东西?

4

1 回答 1

4

在所有情况下,问题都是这样的:

temp = seq1.lower()
seq1 = temp

temp = seq2.lower()

你需要做这个舞蹈而不是仅仅- 正如seq1 = seq1.lower()你在你的问题中指出的那样 - 是因为在 C 上下文中使用 Python 字符串时的注意事项

但是你所做的并不正确,它足以让 Cython认为它是正确的并编译垃圾。

让我们逐行浏览:

temp = seq1.lower()

这会创建一个strout of seq1,调用它的lower(),并将结果存储在temp.

seq1 = temp

这会seq1变成一个指向 中str对象的内部缓冲区的指针temp。正如文档特别说的:

然后,您有责任在必要时保留参考 p。

temp = seq2.lower()

这个 yadda-yadda-yaddas,并将结果存储在temp. 结果,它释放了 的旧值temp。这是您对此的唯一参考str。因此,GC 可以自由地收集它,并且会立即收集。这意味着seq1现在指向已释放对象的内部缓冲区。

前两次,您显然很幸运,并且该缓冲区没有被重用。但最终,在while循环中,它失败了,缓冲区被重用,你最终得到一个指向其他字符串缓冲区的指针。


那么,你如何解决这个问题?

好吧,只要需要,您就可以保留所有这些中间引用。

但实际上,你为什么需要seq1seq2成为char*价值观呢?您不会从中获得任何性能优势。事实上,您会从中获得额外的性能成本。每次使用seq1as astr时,它都会从该缓冲区中创建一个新str对象(并复制缓冲区),即使您已经拥有一个非常好的对象,如果您没有欺骗 Cython,您可以保留它。

因此,最简单的解决方法是将第一行替换为:

cpdef unsigned int func(char *sequence1, char *sequence2, bool case_sensitive=True):
    seq1, seq2 = str(sequence1), str(sequence2)

(你真的不需要str那里的电话;你没有cdef变量的事实应该足够了。但我认为这使意图更清楚。)

于 2013-04-13T00:17:03.877 回答