Как распаковать кортеж длиной n в m <n переменных

В Python 3 я могу сделать следующее (см. Также PEP3132 по расширенной повторяемой распаковке):

a, *b = (1, 2, 3)
# a = 1; b = (2, 3)

Что я могу сделать, чтобы добиться такой же элегантности в Python 2.x?


Я знаю, что могу использовать одноэлементные операции доступа и нарезки, но мне интересно, есть ли более питонический способ. Мой код до сих пор:

a, b = (1, 2, 3)[0], (1, 2, 3)[1:]
# a = 1; b = (2, 3)

5 ответов

Решение

Я обнаружил, что связанный PEP3132 также дает несколько примеров для Python 2.x:

Многие алгоритмы требуют разбиения последовательности в паре "первый, оставшийся":

first, rest = seq[0], seq[1:]

[...]

Кроме того, если правое значение является не списком, а итеративным, его необходимо преобразовать в список, прежде чем можно будет выполнять нарезку; чтобы избежать создания этого временного списка, нужно прибегнуть к

it = iter(seq)
first = it.next()
rest = list(it)

Другие подходы приведены в ответах на этот вопрос:

Функция Аргумент Список Распаковка Подход

требует дополнительного определения функции / вызова:

def unpack(first, *rest): 
  return first, rest
first, rest = unpack( *seq )

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

Генераторный подход

Кредиты Также требуется реализация пользовательской функции. Немного более гибок относительно количества первых переменных.

def unpack_nfirst(seq, nfirst):
  it = iter(seq)
  for x in xrange(nfirst):
    yield next(it, None)
  yield tuple(it)
first, rest = unpack_nfirst(seq, 1)

Наверное, наиболее питоническими были бы те, что упомянуты в PEP выше?

Я могу ошибаться но насколько я знаю

a, *b = (1, 2, 3)

это просто синтаксический сахар для нарезки и индексации кортежей. Я нахожу это полезным, но не очень явным.

У меня есть эта удобная маленькая функция:

def just(n, seq):
    it = iter(seq)
    for _ in range(n - 1):
        yield next(it, None)
    yield tuple(it)

Например:

a, b, c = just(3, range(5))
print a, b, c
## 0 1 (2, 3, 4)

также работает с меньшим количеством аргументов:

a, b, c = just(3, ['X', 'Y'])
print a, b, c
## X Y ()

В ответ на комментарий вы также можете определить:

def take2(a, *rest): return a, rest
def take3(a, b, *rest): return a, b, rest
def take4(a, b, c, *rest): return a, b, rest
... etc

и используйте это так:

p = (1,2,3)
a, b = take2(*p)
print a, b
## 1 (2, 3)

Я не думаю, что есть лучший способ, чем тот, который вы опубликовали, но здесь есть альтернатива iter

>>> x = (1,2,3)
>>> i = iter(x)
>>> a,b = next(i), tuple(i)
>>> a
1
>>> b
(2, 3)

Не уверен насчет контекста, но как насчет.pop(0)?

Я вижу, что в вашем примере есть кортежи, но если вы хотите делать то, что делаете, списки будут более подходящими, я думаю? (Если только у них нет веских причин для того, чтобы они были неизменными, они не указаны в вопросе.)

b = [1,2,3]
a = b.pop(0)
Другие вопросы по тегам