#python #for-loop #cython
#python #for-цикл #cython
Вопрос:
Я изучаю Cython и наткнулся на этот фрагмент кода:
import numpy as np
cimport numpy as np
def mean(np.ndarray[np.double_t] input):
cdef np.double_t cur
# Py_ssize_t is numpy's index type
cdef Py_ssize_t i
cdef Py_ssize_t N = len(input)
for i from 0 <= i < N:
cur = input[i]
return cur / N
a=np.array([1,2,3,4], dtype=np.double)
Очевидно, что это возвращает среднее значение из a, которое равно 2.5. Мой вопрос заключается в следующем:
Является ли цикл for циклом Python, Cython или C?
Ответ №1:
Скомпилируйте его и посмотрите: код на C, который создает Cython, красиво аннотирован.
/* "cyexample.pyx":11
* cdef Py_ssize_t N = len(input)
*
* for i from 0 <= i < N: # <<<<<<<<<<<<<<
* cur = input[i]
*
*/
__pyx_t_1 = __pyx_v_N;
for (__pyx_v_i = 0; __pyx_v_i < __pyx_t_1; __pyx_v_i ) {
/* "cyexample.pyx":12
*
* for i from 0 <= i < N:
* cur = input[i] # <<<<<<<<<<<<<<
*
* return cur / N
*/
__pyx_t_2 = __pyx_v_i;
__pyx_t_3 = -1;
if (__pyx_t_2 < 0) {
__pyx_t_2 = __pyx_bshape_0_input;
if (unlikely(__pyx_t_2 < 0)) __pyx_t_3 = 0;
} else if (unlikely(__pyx_t_2 >= __pyx_bshape_0_input)) __pyx_t_3 = 0;
if (unlikely(__pyx_t_3 != -1)) {
__Pyx_RaiseBufferIndexError(__pyx_t_3);
{__pyx_filename = __pyx_f[0]; __pyx_lineno = 12; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
}
__pyx_v_cur = (__pyx_v_cur (*__Pyx_BufPtrStrided1d(__pyx_t_5numpy_double_t *, __pyx_bstruct_input.buf, __pyx_t_2, __pyx_bstride_0_input)));
}
И таким образом, сам цикл успешно превращается в C. Обратите внимание, что в наши дни Cython может обрабатывать диапазон естественным образом, поэтому старый стиль «from 0 <= i < N» не является необходимым. Смысл введения (не-Python) синтаксиса «for / from» состоял в том, чтобы указать, какие циклы должны быть C-ified.
Комментарии:
1. Я выполнил несколько наивных тестов timeit, и обе структуры цикла, похоже, выполняются примерно за одинаковое время при большом размере массива. Кто-нибудь может это подтвердить? Мне просто любопытно, почему автор этого конкретного фрагмента решил использовать старую структуру в современном коде.
2. Раньше Cython не проводил оптимизацию
for i in range(10)
, поэтому он фактически создавал список длиной 10 и выполнял итерации по нему с использованием обратных вызовов Python. В связи с этим Pyrex / Cython ввел for … От… синтаксис, который сводился бы к C. Я иногда все еще использую старый синтаксис, потому что более понятно, что этот код оптимизирован.
Ответ №2:
for..from
похоже, это цикл Pyrex / Cython: http://docs.cython.org/src/userguide/language_basics.html#integer-for-loops
Комментарии:
1. Правильно. Это Pyrex-форма цикла for, которую Cython сохранил для обратной совместимости