3

まず、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 のバグですか、それとも些細なことを見逃しているだけですか?

4

1 に答える 1

4

問題は、次のようなすべての場合にあります。

temp = seq1.lower()
seq1 = temp

temp = seq2.lower()

seq1 = seq1.lower()質問で指摘したように、このダンスを行う必要がある理由は、 C コンテキストで Python 文字列を使用する場合の警告のためです。

しかし、あなたがしていることは正しくありません。Cython をだまして正しい思い込ませ、ゴミをコンパイルさせるには十分です。

行ごとに見ていきましょう:

temp = seq1.lower()

これにより、strout が作成されseq1、その が呼び出されlower()、結果が に保存されtempます。

seq1 = temp

これは、 のオブジェクトseq1の内部バッファへのポインタになります。ドキュメントが具体的に言うように:strtemp

その場合、必要なだけ参照 p を保持するのはあなたの責任です。

temp = seq2.lower()

この yadda-yadda-yaddas を実行し、結果を に格納しtempます。その結果、 の古い値が解放されますtemp。あなたがそれに対して持っていた唯一の参照でしたstr。したがって、GC はそれを自由に収集でき、すぐに収集します。これはseq1、解放されたオブジェクトの内部バッファーを指していることを意味します。

最初の 2 回は、明らかに幸運で、そのバッファーは再利用されません。しかし、最終的にはwhileループ内で失敗し、バッファーが再利用され、別の文字列バッファーへのポインターになってしまいます。


それで、あなたはこれをどのように解決しますか?

まあ、必要な限り、これらすべての中間参照を保持できます。

しかし、本当に、なぜあなたは必要seq1であり、seq2とにかくchar*価値があるのでしょうか? パフォーマンス上の利点は得られません。実際、追加のパフォーマンスコストが発生しています。seq1asを使用するたびに、Cython をだまさなければ代わりに保持できた完全に優れたオブジェクトが既にあるにもかかわらず、そのバッファーからstr新しいオブジェクトが作成されます (そしてバッファーがコピーされます)。str

したがって、最も簡単な修正は、最初の行を次のように置き換えることです。

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 に答える