Этот ответ касается только реализации CPython. Использование понимания списка быстрее, поскольку генератор сначала все равно преобразуется в список. Это сделано потому, что длина последовательности должна быть определена за до продолжения замены данных, и генератор не может сказать вам его длину.
Для назначения фрагмента списка эта операция обрабатывается забавно названным list_ass_slice
. Существует специальная обработка для назначения списка или кортежа, здесь - они могут использовать PySequence_Fast
ops.
Эта является 3.7 реализацией PySequence_Fast
, где вы можете ясно увидеть проверку типов для списка или кортежей:
PyObject *
PySequence_Fast(PyObject *v, const char *m)
{
PyObject *it;
if (v == NULL) {
return null_error();
}
if (PyList_CheckExact(v) || PyTuple_CheckExact(v)) {
Py_INCREF(v);
return v;
}
it = PyObject_GetIter(v);
if (it == NULL) {
if (PyErr_ExceptionMatches(PyExc_TypeError))
PyErr_SetString(PyExc_TypeError, m);
return NULL;
}
v = PySequence_List(it);
Py_DECREF(it);
return v;
}
Выражение генератора не выполнит эту проверку типа и перейдет к резервному коду, где оно преобразуется в объект списка, так что длина может быть предопределена .
В общем случае желательна заранее определенная длина для эффективного распределения хранилища списков, а также для предоставления полезных сообщений об ошибках с расширенным назначением срезов:
>>> vals = (x for x in 'abc')
>>> L = [1,2,3]
>>> L[::2] = vals # attempt assigning 3 values into 2 positions
---------------------------------------------------------------------------
Traceback (most recent call last)
...
ValueError: attempt to assign sequence of size 3 to extended slice of size 2
>>> L # data unchanged
[1, 2, 3]
>>> list(vals) # generator was fully consumed
[]