I accidentally happened to write something like cdef list[float] l
It turned out that this can be successfully compiled, but only as a local variable inside a function as with any other buffer variable. I looked at generated code, and it's more complicated than with just cdef list l
- extra code also had to do something with buffer.
So, is this a Python list, but with buffer protocol? What advantages does it provide? What are the use cases?
Please point me to official documentation, if it is documented somewhere (I failed to find)
I've now had a chance to look at the generated code.
On Cython 0.29.x this gets interpreted using the old-style np.ndarray
syntax - it adds a type check that l
is both a list
, and is a type with the buffer protocol containing a 1D array of float
. Such a check will never pass and thus the code is useless.
The old np.ndarray
buffer code predates type annotations like list[float]
by a long time so didn't anticipate that it'd develop an alternative meaning.
Cython 3 is a bit cleverer about handling subscripted types. It simply interprets the type and list
as ignores the float
part. Since Cython can't enforce the element type there's nothing useful it can do with it.
Some example Cython 3 code (since you claim in the comments that it's different)
For list[float]
:
/* "lf.pyx":6
* def g(x):
* cdef list[float] y = x # <<<<<<<<<<<<<<
* z = y[0]
*/
if (!(likely(PyList_CheckExact(__pyx_v_x))||((__pyx_v_x) == Py_None) || __Pyx_RaiseUnexpectedTypeError("list", __pyx_v_x))) __PYX_ERR(0, 6, __pyx_L1_error)
__pyx_t_1 = __pyx_v_x;
__Pyx_INCREF(__pyx_t_1);
__pyx_v_y = ((PyObject*)__pyx_t_1);
__pyx_t_1 = 0;
/* "lf.pyx":7
* def g(x):
* cdef list[float] y = x
* z = y[0] # <<<<<<<<<<<<<<
*/
if (unlikely(__pyx_v_y == Py_None)) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable");
__PYX_ERR(0, 7, __pyx_L1_error)
}
__pyx_t_1 = __Pyx_GetItemInt_List(__pyx_v_y, 0, long, 1, __Pyx_PyInt_From_long, 1, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 7, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_v_z = __pyx_t_1;
__pyx_t_1 = 0;
For list
/* "lf.pyx":2
* def f(x):
* cdef list y = x # <<<<<<<<<<<<<<
* z = y[0]
*
*/
if (!(likely(PyList_CheckExact(__pyx_v_x))||((__pyx_v_x) == Py_None) || __Pyx_RaiseUnexpectedTypeError("list", __pyx_v_x))) __PYX_ERR(0, 2, __pyx_L1_error)
__pyx_t_1 = __pyx_v_x;
__Pyx_INCREF(__pyx_t_1);
__pyx_v_y = ((PyObject*)__pyx_t_1);
__pyx_t_1 = 0;
/* "lf.pyx":3
* def f(x):
* cdef list y = x
* z = y[0] # <<<<<<<<<<<<<<
*
* def g(x):
*/
if (unlikely(__pyx_v_y == Py_None)) {
PyErr_SetString(PyExc_TypeError, "'NoneType' object is not subscriptable");
__PYX_ERR(0, 3, __pyx_L1_error)
}
__pyx_t_1 = __Pyx_GetItemInt_List(__pyx_v_y, 0, long, 1, __Pyx_PyInt_From_long, 1, 0, 1); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 3, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_1);
__pyx_v_z = __pyx_t_1;
__pyx_t_1 = 0;