2013-04-12 1 views
3

Во-первых, я использую Cython 0.18 с Python 2.7.4. Я испытываю довольно странную ошибку, и я не уверен, почему. Вот код игрушка:неожиданный вывод и возвращаемое значение с Cython

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 

Что, черт возьми, goi ng здесь? Прежде всего, почему вывод stdout? Почему я не получаю вывод, который должен получить? Это ошибка Cython, или я просто пропустил что-то тривиальное здесь?

+1

Я замечаю, что вы используете 'seq1' и' seq2' как объекты 'str' с самого начала, и я буду держать пари, если вы добавите явное преобразование (изменение params to 's1' и' s2', тогда напишите 'str1, str2 = str (s1), str (s2)' вверху), проблема исчезнет. (По крайней мере, стоит проверить.) – abarnert

+0

Это обходное решение, необходимое для взаимодействия с объектами '' char * '' и Python '' str''. У меня есть комментарий в коде, который описывает, почему я это делаю. Ваша идея литья seq1 и seq2 на str интересна. Я попробую. Вернитесь назад ... – Geoff

+1

Да, я понял, что часть и удалил комментарий. Но это вдохновило второй комментарий. Вы не получаете каких-либо преимуществ от использования char * внутри - на самом деле, я думаю, что вы делаете это медленнее. Для каждого промежуточного шага вы создаете объект 'str' Python, выполняете' str' операции, сохраняете результаты 'str', а затем делаете' seq1' в этом буфере 'str'. И затем вы создаете новую 'str' из' seq1', когда вы могли бы просто вернуть старый, если бы вы не обманули Cython. – abarnert

ответ

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. Как конкретно говорится в документах:

Это ваша обязанность держать ссылку p столько, сколько необходимо.

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 переменных должен быть достаточно. Но я думаю, что это делает намерение более ясным.)

 Смежные вопросы

  • Нет связанных вопросов^_^