Частенько бывает нужно проитерироваться по большой коллекции в которой лежат другие коллекции.

Возьмем для примера вот эту:

In [32]: L = [ [i] for i in range(10) ]

In [33]: L
Out[33]: [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]

Очевидный способ пробежать по вложенным элементам - это чтение по индексу:

In [24]: [ i[0] for i in L ]
Out[24]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Но есть и другой способ сделать нечто похожее, со своими особенностями и преимуществами:

In [24]: [ i for i, in L ]
Out[24]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

По сути, это хитро записанная распаковка листа/тупла, состоящего строго из одного элемента.

Это работает потому что, в python конструктором тупла является запятая, а не скобки.

In [29]: 5,
Out[29]: (5,)

In [30]: (5)
Out[30]: 5

А есть отличия?

Да.

Кажется, что сложно придумать что-то быстрее, чем чтение по индесу из начала списка - но распаковка оказывается быстрее, процентов на 10.

In [24]: L = [ [i] for i in range(1000) ]

In [25]: %timeit [ i for i, in L  ]
19.7 µs ± 31.4 ns per loop (mean ± std. dev. of 7 runs, 100,000 loops each)

In [26]: %timeit [ i[0] for i in L  ]
22.1 µs ± 150 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

Есть и еще кое-что.

Если на вход попадут пустые списки, то оба варианта упадут с ошибками, правда, каждый со своей:

In [30]: [ i[0] for i in L+[[]] ]

# я удалил traceback, вы и так читаете Traceback :)

IndexError: list index out of range

In [31]: [ i for i, in L+[[]] ]

# я удалил traceback, вы и так читаете Traceback :)

ValueError: not enough values to unpack (expected 1, got 0)

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

Что еще почитать?

Доки python на тему unpacking - тык, тык и тык

Наш телеграм канал