2013-04-12 3 views
3

먼저 Cython 0.18을 Python 2.7.4와 함께 사용하고 있습니다. 나는 이상한 버그를 겪고 있는데 왜 그런지 모르겠습니다. 내가 있는지 기대, 이제Cython에서 예기치 않은 출력 및 반환 값

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

다음 : 여기

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) 

샘플 호출 것 : 여기에 장난감 코드는

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 버그입니까, 아니면 여기에 사소한 것을 놓친 것입니까?

+1

처음부터'seq1'과'seq2'를'str' 객체로 사용하고 있음을 알게되었습니다. 명시 적 변환 (변경 params를's1'과's2'로 바꾸고,'str1, str2 = str (s1), str (s2)'를 쓰면 문제는 사라집니다. (최소한 테스트할만한 가치가 있습니다.) – abarnert

+0

''char *''와 Python''str'' 객체와 상호 작용하는 데 필요한 임시 해결책입니다. 내가 왜 그렇게하는지 설명하는 코드에 주석이 있습니다. seq1과 seq2를 str에 캐스팅하는 것이 흥미로운 아이디어입니다. 시도해 볼게. 바로 돌아 가라. ... – Geoff

+1

그래, 나는 그 부분을 알아 내고 코멘트를 삭제했다. 그러나 그것이 두 번째 코멘트에 영감을주었습니다. 내부적으로 char *를 사용하면 성능상의 이점을 얻지 못할 것입니다. 실제로 느리게 만드는 것 같습니다. 각각의 중간 단계에서 파이썬'str' 객체를 만들고,'str' 연산을 수행하고,'str' 결과를 저장 한 다음,'str' 버퍼에'seq1' 포인트를 만듭니다. 그리고 Cython을 속이지 않았다면 오래된 것을 다시 살펴볼 수있을 때'seq1'에서 새로운'str'을 만듭니다. – abarnert

답변

4

문제는이 같은 모든 경우에 :

temp = seq1.lower() 
seq1 = temp 

temp = seq2.lower() 

이유는 당신은 당신이 당신의 지적 -as 대신 seq1 = seq1.lower()이 무용 할 필요가 있기 때문에의 Caveats when using a Python string in a C context 질문을-입니다.

당신이하고있는 일이 정확하지 않습니다. Cython을 로 생각하고,으로 생각하고 쓰레기를 컴파일하는 것으로 충분합니다. 라인별로 라인을 통해

하자 단계 :

temp = seq1.lower() 

seq1 밖으로 str, 그 lower()를 호출하여 생성하고, temp에 결과를 저장합니다.

seq1 = temp 

이것은 temp에서 str 오브젝트의 내부 버퍼에 대한 포인터로 seq1한다. 문서가 구체적으로 말하듯이 :

필요한만큼 오랫동안 참고 p를 보유하는 것은 귀하의 책임입니다.

temp = seq2.lower() 

이-옹알 옹알-yaddas, 저장 temp의 결과. 결과적으로 이전 값인 temp이 해제됩니다. 그게 유일한 참조 번호는 str입니다. 따라서 GC는 무료로 수집 할 수 있으며 즉시 그렇게합니다. 즉, seq1은 해제 된 개체의 내부 버퍼를 가리키고 있습니다.

처음 두 번은 분명히 운이 좋았고 버퍼는 다시 사용되지 않습니다. 그러나 결국 while 루프에서는 실패하고 버퍼는 재사용되며 다른 문자열 버퍼에 대한 포인터로 끝납니다.


그래서 어떻게 해결할 수 있습니까?

글쎄, 당신은 모든 중급 참고 자료가 필요할 때까지 둘 수 있습니다.

하지만 실제로 seq1seq2이 어쨌든 char* 값이 필요합니까? 당신은 그것으로부터 어떠한 성과 이득도 얻지 못하고 있습니다. 사실 비용이이됩니다. seq1str으로 사용할 때마다, 그 버퍼에서 새로운 str 개체가 생성됩니다 (버퍼를 복사하는 것). Cython을 속이지 않았 더라면 방금 대신 완벽하게 유지할 수있는 완벽하게 좋은 것이 있었지만 .

그래서, 가장 쉬운 수정으로 첫 번째 라인을 교체하는 것입니다 :

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

(당신은 정말 거기 str 전화를 필요가 없습니다 당신이 한 사실은 cdef 변수가 충분해야하지. 하지만 이것은 의도를 명확하게 만든다고 생각합니다.)