Понимание *x,= lst
Я перебираю какой-то старый код, пытаясь понять, что он делает, и наткнулся на это странное утверждение:
*x ,= p
p
это список в этом контексте. Я пытался выяснить, что делает это утверждение. Насколько я могу сказать, это просто устанавливает x
к стоимости p
, Например:
p = [1,2]
*x ,= p
print(x)
Просто дает
[1, 2]
Так чем это отличается от x = p
? Есть идеи, что делает этот синтаксис?
3 ответа
*x ,= p
в основном запутанная версия x = list(p)
используя расширенную повторяемую распаковку. Запятая после x
требуется, чтобы назначение назначения было кортежем (хотя это также может быть список).
*x, = p
отличается от x = p
потому что первый создает копию p
(т.е. новый список), в то время как последний создает ссылку на исходный список. Проиллюстрировать:
>>> p = [1, 2]
>>> *x, = p
>>> x == p
True
>>> x is p
False
>>> x = p
>>> x == p
True
>>> x is p
True
Эта функция была представлена в Python 3.0 ( PEP 3132). В Python 2 вы можете сделать что-то вроде этого:
>>> p = [1, 2, 3]
>>> q, r, s = p
>>> q
1
>>> r
2
>>> s
3
Python 3 расширил это, чтобы одна переменная могла содержать несколько значений:
>>> p = [1, 2, 3]
>>> q, *r = p
>>> q
1
>>> r
[2, 3]
Это то, что используется здесь. Однако вместо двух переменных для хранения трех значений это просто одна переменная, которая принимает каждое значение в списке. Это отличается от x = p
так как x = p
просто означает, что x
это другое имя для p
, В этом случае, однако, это новый список, который просто содержит те же значения. (Возможно, вас заинтересуют "Наименьшее изумление" и изменяемый аргумент по умолчанию)
Два других распространенных способа создания этого эффекта:
>>> x = list(p)
а также
>>> x = p[:]
Начиная с Python 3.3, у объекта списка фактически есть метод, предназначенный для копирования:
x = p.copy()
На самом деле срез очень похожая концепция. Однако, как указал nneonneo, это работает только с такими объектами, как списки и кортежи, которые поддерживают срезы. Однако упомянутый вами метод работает с любыми итерациями: словарями, наборами, генераторами и т. Д.
Вы должны всегда бросать это dis
и посмотри, что это отбросит к тебе; вы увидите, как*x, = p
на самом деле отличается отx = p
:
dis('*x, = p')
1 0 LOAD_NAME 0 (p)
2 UNPACK_EX 0
4 STORE_NAME 1 (x)
В то время как простой оператор присваивания:
dis('x = p')
1 0 LOAD_NAME 0 (p)
2 STORE_NAME 1 (x)
(Снятие не связанныхNone
возвраты)
Как вы видете UNPACK_EX
это другой код операции между ними; это задокументировано как:
Реализует присваивание с помеченной звездочкой целью: распаковывает итерируемое в TOS (начало стека) в отдельные значения, где общее количество значений может быть меньше, чем количество элементов в итерируемом: одно из новых значений будет списком всех остатки предметов.
Вот почему, как заметил Юджин, вы получаете новый объект, который называется по имени x
а не ссылка на уже существующий объект (как в случае с x = p
).
*x,
действительно кажется очень странным (лишняя запятая там и все), но это требуется здесь. Левая часть должна быть либо кортежем, либо списком, и из-за причудливости создания кортежа из одного элемента в Python вам необходимо использовать завершающий ,
:
i = 1, # one element tuple
Если вы любите сбивать с толку людей, вы всегда можете использовать list
версия этого:
[*x] = p
который делает то же самое, но без лишней запятой.
Вы можете ясно понять это из примера ниже
L = [1, 2, 3, 4]
while L:
temp, *L = L
print(temp, L)
что он делает, переменная front будет каждый раз получать первый элемент, а оставшийся список будет передан L.
Результат будет выглядеть, как показано ниже.
1 [2, 3, 4]
2 [3, 4]
3 [4]
4 []
Также посмотрите на пример ниже
x, *y, z = "python"
print(x,y,z)
В этом случае оба x,z получат каждую букву из строки, означающую, что первая буква будет присвоена x, а последняя буква будет назначена z, а оставшаяся строка будет назначена переменной y.
p ['y', 't', 'h', 'o'] n
Еще один пример,
a, b, *c = [0,1,2,3]
print(a,b,c)
0 1 [2,3]
Граничный случай: если для звездной переменной ничего не осталось, она получит пустой список.
Пример:
a,b=[1]
print(a,b)
1 []