#python #python-3.x #iterable-unpacking #argument-unpacking
Вопрос:
Рассмотрим следующие выражения. Обратите внимание, что некоторые выражения повторяются для представления «контекста».
(это длинный список)
a, b = 1, 2 # simple sequence assignment
a, b = ['green', 'blue'] # list asqignment
a, b = 'XY' # string assignment
a, b = range(1,5,2) # any iterable will do
# nested sequence assignment
(a,b), c = "XY", "Z" # a = 'X', b = 'Y', c = 'Z'
(a,b), c = "XYZ" # ERROR -- too many values to unpack
(a,b), c = "XY" # ERROR -- need more than 1 value to unpack
(a,b), c, = [1,2],'this' # a = '1', b = '2', c = 'this'
(a,b), (c,) = [1,2],'this' # ERROR -- too many values to unpack
# extended sequence unpacking
a, *b = 1,2,3,4,5 # a = 1, b = [2,3,4,5]
*a, b = 1,2,3,4,5 # a = [1,2,3,4], b = 5
a, *b, c = 1,2,3,4,5 # a = 1, b = [2,3,4], c = 5
a, *b = 'X' # a = 'X', b = []
*a, b = 'X' # a = [], b = 'X'
a, *b, c = "XY" # a = 'X', b = [], c = 'Y'
a, *b, c = "X...Y" # a = 'X', b = ['.','.','.'], c = 'Y'
a, b, *c = 1,2,3 # a = 1, b = 2, c = [3]
a, b, c, *d = 1,2,3 # a = 1, b = 2, c = 3, d = []
a, *b, c, *d = 1,2,3,4,5 # ERROR -- two starred expressions in assignment
(a,b), c = [1,2],'this' # a = '1', b = '2', c = 'this'
(a,b), *c = [1,2],'this' # a = '1', b = '2', c = ['this']
(a,b), c, *d = [1,2],'this' # a = '1', b = '2', c = 'this', d = []
(a,b), *c, d = [1,2],'this' # a = '1', b = '2', c = [], d = 'this'
(a,b), (c, *d) = [1,2],'this' # a = '1', b = '2', c = 't', d = ['h', 'i', 's']
*a = 1 # ERROR -- target must be in a list or tuple
*a = (1,2) # ERROR -- target must be in a list or tuple
*a, = (1,2) # a = [1,2]
*a, = 1 # ERROR -- 'int' object is not iterable
*a, = [1] # a = [1]
*a = [1] # ERROR -- target must be in a list or tuple
*a, = (1,) # a = [1]
*a, = (1) # ERROR -- 'int' object is not iterable
*a, b = [1] # a = [], b = 1
*a, b = (1,) # a = [], b = 1
(a,b),c = 1,2,3 # ERROR -- too many values to unpack
(a,b), *c = 1,2,3 # ERROR - 'int' object is not iterable
(a,b), *c = 'XY', 2, 3 # a = 'X', b = 'Y', c = [2,3]
# extended sequence unpacking -- NESTED
(a,b),c = 1,2,3 # ERROR -- too many values to unpack
*(a,b), c = 1,2,3 # a = 1, b = 2, c = 3
*(a,b) = 1,2 # ERROR -- target must be in a list or tuple
*(a,b), = 1,2 # a = 1, b = 2
*(a,b) = 'XY' # ERROR -- target must be in a list or tuple
*(a,b), = 'XY' # a = 'X', b = 'Y'
*(a, b) = 'this' # ERROR -- target must be in a list or tuple
*(a, b), = 'this' # ERROR -- too many values to unpack
*(a, *b), = 'this' # a = 't', b = ['h', 'i', 's']
*(a, *b), c = 'this' # a = 't', b = ['h', 'i'], c = 's'
*(a,*b), = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5, 6, 7]
*(a,*b), *c = 1,2,3,3,4,5,6,7 # ERROR -- two starred expressions in assignment
*(a,*b), (*c,) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable
*(a,*b), c = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5, 6], c = 7
*(a,*b), (*c,) = 1,2,3,4,5,'XY' # a = 1, b = [2, 3, 4, 5], c = ['X', 'Y']
*(a,*b), c, d = 1,2,3,3,4,5,6,7 # a = 1, b = [2, 3, 3, 4, 5], c = 6, d = 7
*(a,*b), (c, d) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable
*(a,*b), (*c, d) = 1,2,3,3,4,5,6,7 # ERROR -- 'int' object is not iterable
*(a,*b), *(c, d) = 1,2,3,3,4,5,6,7 # ERROR -- two starred expressions in assignment
*(a,b), c = 'XY', 3 # ERROR -- need more than 1 value to unpack
*(*a,b), c = 'XY', 3 # a = [], b = 'XY', c = 3
(a,b), c = 'XY', 3 # a = 'X', b = 'Y', c = 3
*(a,b), c = 'XY', 3, 4 # a = 'XY', b = 3, c = 4
*(*a,b), c = 'XY', 3, 4 # a = ['XY'], b = 3, c = 4
(a,b), c = 'XY', 3, 4 # ERROR -- too many values to unpack
Как правильно вывести результат таких выражений вручную?
Комментарии:
1. Честно говоря, большинство из них гораздо сложнее, чем то, что вы видите в коде каждый день. Изучите основы распаковки списков/кортежей, и все будет в порядке.
2. Обратите внимание, что они рекурсивны. Так что, если вы не справитесь с первыми несколькими, вы справитесь со всем. Попробуйте заменить, например, *( *a, b) на *x, выясните, что x распаковывает, а затем подключите (*a, b) обратно для x и т. Д.
3. @greengit Я считаю, что обладаю глубокими знаниями Python, и я просто знаю общие правила 🙂 Вам не обязательно знать каждый конкретный случай, вам просто иногда нужно запустить переводчика и что-то проверить.
4. Вау, отличный список. Я действительно не знал о
a, *b = 1, 2, 3
том, как распаковывать вещи. Но это Py3k, верно ?
Ответ №1:
Мои извинения за длину этого поста, но я решил сделать выбор в пользу полноты.
Как только вы знаете несколько основных правил, их нетрудно обобщить. Я сделаю все возможное, чтобы объяснить это на нескольких примерах. Поскольку вы говорите об оценке этих «вручную», я предложу несколько простых правил замены. В принципе, вам может быть легче понять выражение, если все итеративные объекты отформатированы одинаково.
Только для целей распаковки следующие замены действительны с правой стороны =
(т. е. для значений r):
'XY' -> ('X', 'Y')
['X', 'Y'] -> ('X', 'Y')
Если вы обнаружите, что значение не распаковывается, вы отмените замену. (Дополнительные пояснения см. Ниже.)
Кроме того, когда вы видите «голые» запятые, представьте, что есть кортеж верхнего уровня. Сделайте это как с левой, так и с правой стороны (т. Е. для значений lvalues и rvalues).:
'X', 'Y' -> ('X', 'Y')
a, b -> (a, b)
Имея в виду эти простые правила, вот несколько примеров:
(a,b), c = "XY", "Z" # a = 'X', b = 'Y', c = 'Z'
Applying the above rules, we convert "XY"
to ('X', 'Y')
, and cover the naked commas in parens:
((a, b), c) = (('X', 'Y'), 'Z')
Визуальное соответствие здесь делает довольно очевидным, как работает задание.
Вот ошибочный пример:
(a,b), c = "XYZ"
Следуя приведенным выше правилам подстановки, мы получаем следующее:
((a, b), c) = ('X', 'Y', 'Z')
Это явно ошибочно; вложенные структуры не совпадают. Теперь давайте посмотрим, как это работает на несколько более сложном примере:
(a,b), c, = [1,2],'this' # a = '1', b = '2', c = 'this'
Применяя вышеуказанные правила, мы получаем
((a, b), c) = ((1, 2), ('t', 'h', 'i', 's'))
Но теперь из структуры ясно, что 'this'
она не будет распакована, а будет назначена непосредственно c
. Поэтому мы отменяем замену.
((a, b), c) = ((1, 2), 'this')
Теперь давайте посмотрим, что произойдет, когда мы завернем c
в кортеж:
(a,b), (c,) = [1,2],'this' # ERROR -- too many values to unpack
Становится
((a, b), (c,)) = ((1, 2), ('t', 'h', 'i', 's'))
Опять же, ошибка очевидна. c
это уже не голая переменная, а переменная внутри последовательности, и поэтому соответствующая последовательность справа распаковывается (c,)
. Но последовательности имеют разную длину, так что есть ошибка.
Теперь для расширенной распаковки используйте *
оператора. Это немного сложнее, но все равно довольно просто. Переменная, которой предшествует, *
становится списком, содержащим любые элементы из соответствующей последовательности, которые не присвоены именам переменных. Начнем с довольно простого примера:
a, *b, c = "X...Y" # a = 'X', b = ['.','.','.'], c = 'Y'
Это становится
(a, *b, c) = ('X', '.', '.', '.', 'Y')
Самый простой способ проанализировать это-работать с самого начала. 'X'
назначается a
и 'Y'
назначается c
. Остальные значения в последовательности помещаются в список и присваиваются b
.
Значения, подобные (*a, b)
и (a, *b)
являющиеся лишь частными случаями вышеизложенного. Вы не можете иметь двух *
операторов внутри одной последовательности значений, потому что это было бы неоднозначно. Куда бы делись ценности в чем-то подобном (a, *b, *c, d)
-в b
или c
? Я сейчас рассмотрю вложенный случай.
*a = 1 # ERROR -- target must be in a list or tuple
Здесь ошибка вполне объяснима. Цель ( *a
) должна находиться в кортеже.
*a, = (1,2) # a = [1,2]
Это работает, потому что там есть голая запятая. Применение правил…
(*a,) = (1, 2)
Поскольку нет других переменных , кроме *a
, *a
поглощает все значения в последовательности rvalue. Что делать, если вы (1, 2)
замените его одним значением?
*a, = 1 # ERROR -- 'int' object is not iterable
становится
(*a,) = 1
Опять же, ошибка здесь объясняется сама собой. Вы не можете распаковать то, что не является последовательностью, и *a
вам нужно что-то распаковать. Поэтому мы изложили это в определенной последовательности
*a, = [1] # a = [1]
Что эквивалентно
(*a,) = (1,)
Наконец, это общая точка путаницы: (1)
это то же самое, что 1
-вам нужна запятая, чтобы отличить кортеж от арифметического оператора.
*a, = (1) # ERROR -- 'int' object is not
Теперь о гнездовании. На самом деле этого примера не было в вашем разделе «ВЛОЖЕННЫЕ»; возможно, вы не осознавали, что он был вложенным?
(a,b), *c = 'XY', 2, 3 # a = 'X', b = 'Y', c = [2,3]
Становится
((a, b), *c) = (('X', 'Y'), 2, 3)
Первое значение в кортеже верхнего уровня присваивается, а остальные значения в кортеже верхнего уровня ( 2
и 3
) присваиваются c
— как и следовало ожидать.
(a,b),c = 1,2,3 # ERROR -- too many values to unpack
*(a,b), c = 1,2,3 # a = 1, b = 2, c = 3
Я уже объяснял выше, почему первая строка выдает ошибку. Вторая строка глупа, но вот почему она работает:
(*(a, b), c) = (1, 2, 3)
Как уже объяснялось ранее, мы работаем с самого начала. 3
присваивается c
, а затем остальные значения присваиваются переменной с *
предшествующим ей, в данном случае, (a, b)
. Так что это эквивалентно тому (a, b) = (1, 2)
, что работает, потому что есть нужное количество элементов. Я не могу придумать ни одной причины, по которой это когда-либо появилось бы в рабочем коде. Аналогично,
*(a, *b), c = 'this' # a = 't', b = ['h', 'i'], c = 's'
становится
(*(a, *b), c) = ('t', 'h', 'i', 's')
Работа с конца, 's'
назначается c
и ('t', 'h', 'i')
назначается (a, *b)
. Снова работает с конца, 't'
назначается a
и ('h', 'i')
назначается b в виде списка. Это еще один глупый пример, который никогда не должен появляться в рабочем коде.
Комментарии:
1. Поскольку ОП привел длинный список примеров, вполне уместно, чтобы вы дали длинный список объяснений.
Ответ №2:
Я нахожу распаковку кортежа Python 2 довольно простой. Каждое имя слева соответствует либо целой последовательности, либо одному элементу в последовательности справа. Если имена соответствуют отдельным элементам любой последовательности, то должно быть достаточно имен, чтобы охватить все элементы.
Однако расширенная распаковка, безусловно, может сбить с толку, потому что она очень мощная. Реальность такова, что вы никогда не должны выполнять последние 10 или более правильных примеров, которые вы привели-если данные настолько структурированы, они должны быть в экземпляре класса dict
или класса, а не в неструктурированных формах, таких как списки.
Очевидно, что новым синтаксисом можно злоупотреблять. Ответ на ваш вопрос заключается в том, что вам не следует читать подобные выражения-это плохая практика, и я сомневаюсь, что они будут использоваться.
То, что вы можете писать произвольно сложные выражения, не означает, что вы должны это делать. Вы могли бы написать такой код, map(map, iterable_of_transformations, map(map, iterable_of_transformations, iterable_of_iterables_of_iterables))
но вы этого не делаете.
Комментарии:
1. Примечание: Я написал такой код, за исключением нескольких более сложных уровней. Это было задумано только как упражнение и сделано с полным осознанием того, что через три месяца это будет бессмысленно для меня и никогда не будет понятно никому другому. Если я правильно помню, он реализовал точку в полигональном тесте, выполнил некоторые преобразования координат и создал несколько SVG, HTML и JavaScript.
Ответ №3:
Если вы считаете, что ваш код может вводить в заблуждение, используйте другую форму для его выражения.
Это похоже на использование дополнительных скобок в выражениях, чтобы избежать вопросов о приоритете операторов. Это всегда хорошая инвестиция, чтобы сделать ваш код читабельным.
Я предпочитаю использовать распаковку только для простых задач, таких как обмен.
Ответ №4:
Первоначальная идея звездного выражения в lhs состоит в том, чтобы улучшить читаемость повторяющейся распаковки, как показано ниже :
first_param, rest_param, third_param = param[0], param[1:-1], param[-1]
Это утверждение равносильно
first_param, *rest_param, third_param = param
В приведенном выше утверждении выражение со звездочкой используется для «перехвата» всех элементов, которые не назначены «обязательным целям» ( first_param
amp; third_param
в этом примере)
Использование выражения со звездочками в lhs имеет следующие правила:
- не более одного звездного выражения в lhs, иначе распаковка не будет уникальной
*a,b,*c = range(5) # wrong
*a,b,c = range(5) # right
a,*b,c = range(5) # right
- Чтобы собрать элементы «rest», выражение со звездочками должно использоваться с обязательными целями. Конечная запятая используется для указания на то, что обязательные цели не существуют
*a = range(5) # wrong
*a, = range(5) # right
Я верю, что если вы освоите эти два правила, вы сможете вывести любой результат выражения со звездочками в lhs.