Понимание * x, = lst
Я просматриваю старый код, пытаясь понять, что он делает, и я наткнулся на это нечетное утверждение:
*x ,= p
p
- это список в этом контексте. Я пытался выяснить, что делает это утверждение. Насколько я могу судить, он просто устанавливает x
значение p
. Например:
p = [1,2]
*x ,= p
print(x)
Просто дает
[1, 2]
Разве это не отличается от x = p
? Любая идея, что делает этот синтаксис?
Ответы
Ответ 1
*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
Ответ 2
Это функция, которая была введена в 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
. В этом случае, однако, это новый список, который просто имеет одинаковые значения в нем. (Возможно, вас заинтересует "Наименьшее изумление" и параметр Mutable Default Argument)
Два других распространенных способа создания этого эффекта:
>>> x = list(p)
и
>>> x = p[:]
Начиная с Python 3.3, у объекта списка фактически есть метод, предназначенный для копирования:
x = p.copy()
Ломтик на самом деле очень похож на концепт. Однако, как указывал nneonneo, это работает только с такими объектами, как списки и кортежи, которые поддерживают срезы. Однако метод, который вы упомянули, работает с любыми итерабельными: словарями, наборами, генераторами и т.д.
Ответ 3
Вы всегда должны бросать их в 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
- это другой op-код между ними; он задокументирован как:
Реализует присвоение целевой звездой: распаковывает итерацию в TOS (верхнюю часть стека) в отдельные значения, где общее количество значений может быть меньше, чем количество элементов в итерабельном: одно из новых значений будет список всех оставшихся элементов.
Вот почему, как отметил Юджин, вы получаете новый объект, на который ссылается имя x
, а не ссылка на уже существующий объект (как в случае с x = p
).
*x,
выглядит очень странно (дополнительная запятая там и все), но она требуется здесь. Левая сторона должна быть либо кортежем, либо списком, и из-за причудливости создания одного элемента кортежа в Python вам нужно использовать трейлинг ,
:
i = 1, # one element tuple
Если вам нравятся запутывающие люди, вы всегда можете использовать версию list
:
[*x] = p
который делает то же самое, но не имеет там дополнительной запятой.