Когда и почему интерпретатор распутывает, предполагая подсписки одинаковой длины?

Я впечатлен и наслаждаюсь тем фактом, что простой оператор Python for может легко распутать список списков без необходимости использования numpy.unravel или эквивалентной функции сглаживания. Однако теперь компромисс заключается в том, что я не могу получить доступ к элементам списка, подобного этому:

for a,b,c in [[5],[6],[7]]:
     print(str(a),str(b),str(c))
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 1)

и вместо этого это работает до длины-1 [5]:

for a,b,c in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]:
     print(a,b,c)

1 2 3
4 5 6
7 8 9
0 0 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: not enough values to unpack (expected 3, got 1)

Логически не имеет смысла предполагать, что список будет иметь фиксированное количество элементов. Почему тогда Python позволяет нам предположить, что список списков всегда будет иметь одинаковое количество элементов?

Я хотел бы знать, что ожидает Python, потому что я хочу предвидеть неправильно отформатированные списки/подсписки.

Я копался в документации Python и Stackoverflow, но не нашел причин или того, как это делает интерпретатор.

Я предполагаю, что сглаживание массивов одинаковой длины является настолько распространенным явлением (например, уменьшение размерности машинного обучения, матричные преобразования и т. д.), что есть полезность в предоставлении этой функции в обмен на невозможность сделать то, что я пробовал. над.


person Dave Liu    schedule 15.03.2019    source источник
comment
for a,b,c in [[5],[6],[7]]: не имеет абсолютно ничего общего с numpy. Это список Python. И for a,b,c in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]: тоже.   -  person roganjosh    schedule 15.03.2019
comment
Во-первых, вы не имеете дело с numpy поведением. Это базовая итерация Python. Во-вторых, вы, кажется, путаете два элемента - итерацию for и распаковку a,b,c. Распаковка негибкая, когда дело доходит до количества ожидаемых элементов, в данном случае 3 (одно значение для каждой переменной). Кроме того, он не позволяет вам ничего предполагать - он увеличивает время выполнения ValueError, если вы ошибаетесь. (это несоответствие не является синтаксической ошибкой).   -  person hpaulj    schedule 15.03.2019
comment
Python ничего не предполагает. Он позволяет распаковывать любой iterable. Вы должны убедиться, что ваши итерации имеют ожидаемое количество элементов.   -  person deceze♦    schedule 15.03.2019
comment
Почему тогда Python позволяет нам предположить, что список списков всегда будет иметь одинаковое количество элементов? - по той же причине он позволяет вам предположить, что список имеет как минимум 3 элемента, когда вы делаете l[2], или почему он позволяет вам считать, что каждый элемент списка является числом, когда вы пишете for x in l: s += x. Почему бы тебе не позволить?   -  person user2357112 supports Monica    schedule 15.03.2019
comment
Есть способы распутать последовательности с неравными подпоследовательностями. См. вопрос zip_longest без fillvalue.   -  person martineau    schedule 16.03.2019
comment
Извините, если мой вопрос был неясен. Я не предполагал, что у этой функции есть ссылка на Numpy. Я просто пытался провести параллели с функциональностью np.unravel Numpy, как предположение о том, что делает интерпретатор. Спасибо @martineau за разъяснение вопроса.   -  person Dave Liu    schedule 18.03.2019


Ответы (3)


Python не предполагает списки одинаковой длины, потому что это касается не только списков.

Когда вы повторяете for a,b,c in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]], происходит то, что python возвращает итератор, который будет повторять (возвращать) каждый список значений.

Так что для эквивалентно:

l = [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]

l_iter = iter(l)

a,b,c = next(l_iter)

next(l_iter) будет возвращать каждый элемент из списка, пока не вызовет исключение StopIteration в соответствии с протоколом итерации Python.

Это означает:

a,b,c = [1,2,3]
a,b,c = [4,5,6]
a,b,c = [7,8,9]
a,b,c = [0,0,0]
a,b,c = [5]

Как видите, теперь python не может распаковать [5] в a,b,c, так как там только одно значение.

person Alexandru Martin    schedule 15.03.2019
comment
Я бы предложил иллюстрировать next(l_iter), а не l_iter.next(); последнее неверно в Python 3 (где имя __next__), в то время как встроенная функция верхнего уровня next() работает в 2.7 и 3.x (и является общепринятым подходом, точно так же len(seq) предпочтительнее seq.__len__(), даже хотя технически оба работают). - person ShadowRanger; 15.03.2019

Интерпретатор всегда предполагает, что длина совпадает при выполнении назначения распаковки, и просто вылетает с ошибкой ValueError, если она не совпадает. Цикл for на самом деле очень похож на своего рода «повторяющийся оператор присваивания», где левая часть является свободной переменной (переменными) цикла, а правая часть представляет собой итерируемый контейнер, дающий последовательное значение (я) для использования на каждом шаге. итерации.

Одно присваивание на итерацию, сделанное в начале тела цикла — в вашем случае это присваивание распаковки, которое связывает несколько имен.

Итак, чтобы быть должным образом эквивалентным второму примеру, ваш первый пример, который был:

for a,b,c in [[5],[6],[7]]:
    ...

вместо этого надо было написать:

for a, in [[5],[6],[7]]:
    ...

Нет «ожидания» и не может быть, потому что (в общем случае) вы можете повторять что угодно, например. поток данных из сокета.

Чтобы полностью понять, как работает цикл for, очень полезна аналогия с операторами присваивания. Все, что вы можете использовать в левой части оператора присваивания, вы можете использовать в качестве цели в цикле for. Например, это эквивалентно установке d[1] = 2 и т. д. в словаре и должно дать тот же результат, что и dict(RHS):

>>> d = {}
>>> for k, d[k] in [[1, 2], [3, 4]]: 
...     pass 
...
>>> d
{1: 2, 3: 4}

Это просто куча заданий в четко определенном порядке.

person wim    schedule 15.03.2019
comment
Не совсем; это должно быть: for [a],[b],[c] in [[[5],[6],[7]]]: (обратите внимание на дополнительные скобки при повторении). В противном случае он будет пытаться распаковать [5] в [a],[b],[c]. - person ShadowRanger; 15.03.2019
comment
Ваш подход к постредактированию тоже работает :-). Как и for [a] in [[5],[6],[7]]:. Я остановлюсь, прежде чем перейду к множеству способов распаковки списков отдельных элементов... :-) - person ShadowRanger; 15.03.2019

Python этого не знает, вы просто сказали ожидать три элемента при распаковке до трех имен. ValueError говорит: «Вы сказали нам три, но мы нашли вложенную итерацию, в которой не было трех элементов, и мы не знаем, что делать».

Python на самом деле не делает ничего особенного для реализации этого; помимо особых случаев для встроенных типов, таких как tuple (и, возможно, list), реализация состоит в том, чтобы просто итерировать вспомогательную итерацию ожидаемое количество раз и сбрасывать все значения, найденные в стеке интерпретатора, а затем сохранять их в предоставленных именах . Он также пытается выполнить итерацию еще раз (ожидая StopIteration), чтобы вы не игнорировали лишние значения.

В ограниченных случаях вы можете быть гибкими, указав перед одним из имен распаковки *, чтобы вы захватили все «не подходящие» элементы в это имя (как list). Это позволяет вам установить минимальное количество элементов, позволяя больше, например. если вам действительно нужен только первый элемент из второго примера, вы можете сделать:

for a, *_ in [[1,2,3],[4,5,6],[7,8,9],[0,0,0], [5]]:
    print(a,b,c)

где _ — это просто имя, которое по соглашению означает «На самом деле меня не волнует это значение, но мне нужно имя-заполнитель».

Другим примером может быть, когда вам нужен первый и последний элемент, но в остальном вам не нужна середина:

for first, *middle, last in myiterable:
    ...

Но в противном случае, если вам нужно обрабатывать итерации переменной длины, не распаковывайте, просто сохраняйте одно имя и повторяйте это имя вручную любым способом, который имеет смысл для логики вашей программы.

person ShadowRanger    schedule 15.03.2019