首先,我在Python 2.7.4中使用Cython 0.18。我遇到了一个相当奇怪的错误,我不知道为什么。这是玩具代码:
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错误,还是我在这里错过了一些微不足道的东西?
答案 0 :(得分:4)
问题在于所有这样的情况:
temp = seq1.lower()
seq1 = temp
temp = seq2.lower()
正如你在问题中指出的那样,你需要做这个舞蹈而不仅仅是seq1 = seq1.lower()
- 是因为Caveats when using a Python string in a C context。
但你正在做的事情是不正确的,它足以让Cython进入思考它是正确的,并编译垃圾。
让我们一步一步地走过去:
temp = seq1.lower()
这会从str
中创建seq1
,调用其lower()
,并将结果存储在temp
中。
seq1 = temp
这使seq1
成为指向str
中temp
对象的内部缓冲区的指针。正如文档具体说的那样:
然后,您有责任在必要时保留参考资料。
temp = seq2.lower()
此yadda-yadda-yaddas,将结果存储在temp
中。结果,它释放了temp
的旧值。这是str
唯一的参考。因此,GC可以自由地收集它,并立即这样做。这意味着seq1
现在指向已释放对象的内部缓冲区。
前两次,你显然很幸运,并且缓冲区不会被重用。但最终,在while
循环中,它失败了,缓冲区被重用,最后你得到一个指向其他字符串缓冲区的指针。
那么,你是如何解决这个问题的?
嗯,只要需要,你就可以保留所有这些中间参考。
但实际上,为什么您还需要seq1
和seq2
为char*
值?你没有从中获得任何性能上的好处。事实上,你正在从中获得额外的性能成本。每次使用seq1
作为str
时,它都会从该缓冲区中创建一个新的str
对象(并复制缓冲区),即使您已经拥有了一个非常好的对象如果你没有欺骗过Cython,那么我只是保留了。
因此,最简单的解决方法是将第一行替换为:
cpdef unsigned int func(char *sequence1, char *sequence2, bool case_sensitive=True):
seq1, seq2 = str(sequence1), str(sequence2)
(你真的不需要那里的str
调用;事实上你没有cdef
这些变量应该足够了。但我认为这使得意图更清晰。)