Python atribuindo várias variáveis ​​ao mesmo valor? comportamento da lista

131

Tentei usar várias atribuições, como mostrado abaixo, para inicializar variáveis, mas fiquei confuso com o comportamento, espero reatribuir a lista de valores separadamente, quero dizer b [0] ec [0] igual a 0 como antes.

a=b=c=[0,3,5]
a[0]=1
print(a)
print(b)
print(c)

O resultado é: [1, 3, 5] [1, 3, 5] [1, 3, 5]

Isso está correto? o que devo usar para várias tarefas? o que é diferente disso?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

resultado: ('f:', 3) ('e:', 4)

Marco
fonte
2
Você quer a, be c,para todos apontam para o mesmo valor (neste caso uma lista), ou você quer a=0, b=3e c=5. Nesse caso, você quer a,b,c = [0,3,5]ou apenas a,b,c = 0,3,5.
Chepner #

Respostas:

271

Se você está vindo para o Python a partir de uma linguagem no C / Java / etc. família, pode ajudar você a parar de pensar acomo uma "variável" e começar a pensar nela como um "nome".

a,, be cnão são variáveis ​​diferentes com valores iguais; são nomes diferentes para o mesmo valor idêntico. Variáveis ​​têm tipos, identidades, endereços e todos os tipos de coisas assim.

Os nomes não têm nada disso. Os valores , é claro, e você pode ter muitos nomes para o mesmo valor.

Se você der Notorious B.I.G.um cachorro quente, * Biggie Smallse Chris Wallacetiver um cachorro quente. Se você alterar o primeiro elemento de apara 1, os primeiros elementos de be csão 1.

Se você quiser saber se dois nomes estão nomeando o mesmo objeto, use o isoperador:

>>> a=b=c=[0,3,5]
>>> a is b
True

Você então pergunta:

o que é diferente disso?

d=e=f=3
e=4
print('f:',f)
print('e:',e)

Aqui, você está religando o nome eao valor 4. Isso não afeta os nomes de de fforma alguma.

Na sua versão anterior, você estava atribuindo a a[0], e não a a. Então, do ponto de vista de a[0], você está religando a[0], mas do ponto de vista a, você está alterando isso no local.

Você pode usar a idfunção, que fornece um número único que representa a identidade de um objeto, para ver exatamente qual objeto é qual, mesmo quando isnão pode ajudar:

>>> a=b=c=[0,3,5]
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261120
>>> id(b[0])
4297261120

>>> a[0] = 1
>>> id(a)
4473392520
>>> id(b)
4473392520
>>> id(a[0])
4297261216
>>> id(b[0])
4297261216

Observe que a[0]foi alterado de 4297261120 para 4297261216 - agora é um nome para um valor diferente. E b[0]agora também é um nome para esse mesmo novo valor. Isso porque ae bainda estão nomeando o mesmo objeto.


Nos bastidores, a[0]=1está realmente chamando um método no objeto de lista. (É equivalente a a.__setitem__(0, 1).) Portanto, não está realmente religando nada. É como ligar my_object.set_something(1). Claro, provavelmente o objeto está religando um atributo de instância para implementar esse método, mas não é isso que é importante; o importante é que você não está atribuindo nada, apenas está mudando o objeto. E é o mesmo com a[0]=1.


user570826 perguntou:

E se tivermos, a = b = c = 10

É exatamente a mesma situação que a = b = c = [1, 2, 3]: você tem três nomes para o mesmo valor.

Mas neste caso, o valor é um int, e ints são imutáveis. Em ambos os casos, você pode religar apara um valor diferente (por exemplo, a = "Now I'm a string!"), mas a não afetará o valor original, que be cainda será nomes para. A diferença é que, com uma lista, você pode alterar o valor [1, 2, 3]em [1, 2, 3, 4]fazendo, por exemplo, a.append(4); já que isso está realmente alterando o valor para o qual os nomes be cos nomes bserão , agora b [1, 2, 3, 4]. Não há como mudar o valor 10para qualquer outra coisa. 10tem 10 para sempre, assim como Claudia, a vampira, tem 5 para sempre (pelo menos até que ela seja substituída por Kirsten Dunst).


* Aviso: não dê um cachorro-quente ao Notorious BIG. Os zumbis do gangsta rap nunca devem ser alimentados depois da meia-noite.

abarnert
fonte
E se nós have, a = b = c = 10;e quando tentamos atualizar o valor de b, ele efetua outro? embora eu verifiquei seus IDs são os mesmos.
AJ
@ user570826: 10é imutável - isso significa que não há como atualizar o valor; portanto, sua pergunta não faz sentido. Você pode apontar bpara um valor diferente, mas isso não afeta ae c, que ainda apontam para o valor original. A diferença que as listas fazem é que elas são mutáveis ​​- por exemplo, você pode appendacessar uma lista ou lst[0] = 3, e isso atualizará o valor, que será visível em todos os nomes desse valor.
abarnert
72

COF cof

>>> a,b,c = (1,2,3)
>>> a
1
>>> b
2
>>> c
3
>>> a,b,c = ({'test':'a'},{'test':'b'},{'test':'c'})
>>> a
{'test': 'a'}
>>> b
{'test': 'b'}
>>> c
{'test': 'c'}
>>> 
Jimmy Kane
fonte
10
IMHO, isso realmente responde à primeira pergunta-chave do OP sobre o que devo usar para várias tarefas, enquanto a resposta mais classificada e mais cerebral acima não.
Will Croxford 13/02/19
2
Ou a,b,c = 1,2,3sem colchetes funciona em Python 2 ou 3, se você realmente deseja esse cm extra de legibilidade.
Will Croxford
14

Sim, esse é o comportamento esperado. a, bec estão todos definidos como rótulos para a mesma lista. Se você quiser três listas diferentes, precisará atribuí-las individualmente. Você pode repetir a lista explícita ou usar uma das várias maneiras de copiar uma lista:

b = a[:] # this does a shallow copy, which is good enough for this case
import copy
c = copy.deepcopy(a) # this does a deep copy, which matters if the list contains mutable objects

As instruções de atribuição no Python não copiam objetos - elas vinculam o nome a um objeto, e um objeto pode ter tantos rótulos quanto você definir. Na sua primeira edição, alterando a [0], você está atualizando um elemento da lista única a que a, b e c se referem. No segundo, alterando e, você está mudando e para ser um rótulo para um objeto diferente (4 em vez de 3).

Peter DeGlopper
fonte
13

Em python, tudo é um objeto, também tipos de variáveis ​​"simples" (int, float, etc.).

Quando você altera um valor de variável, na verdade altera o ponteiro e, se você comparar entre duas variáveis, ele compara os ponteiros . (Para deixar claro, ponteiro é o endereço na memória física do computador em que uma variável está armazenada).

Como resultado, quando você altera um valor de variável interna, altera seu valor na memória e afeta todas as variáveis ​​que apontam para esse endereço.

Para seu exemplo, quando você faz:

a = b =  5 

Isso significa que aeb apontam para o mesmo endereço na memória que contém o valor 5, mas quando você faz:

a = 6

Isso não afeta b porque a agora aponta para outro local da memória que contém 6 eb ainda aponta para o endereço da memória que contém 5.

Mas quando você faz:

a = b = [1,2,3]

aeb, novamente, aponta para o mesmo local, mas a diferença é que, se você alterar um dos valores da lista:

a[0] = 2

Altera o valor da memória em que a aponta, mas a ainda aponta para o mesmo endereço que b e, como resultado, b também muda.

Ori Seri
fonte
6
Isso é altamente enganador. Os ponteiros certamente não são visíveis no nível do Python, e pelo menos duas das quatro principais implementações (PyPy e Jython) não as utilizam nem dentro da implementação.
abarnert
1
Você pode ler e explorar as informações internas do python e descobrirá que todas as variáveis ​​do python são realmente ponteiras.
Ori Seri
4
Não. Em uma implementação do Python (CPython), toda variável é um ponteiro para a PyObject. Isso não é verdade em outras implementações como PyPy ou Jython. (Na verdade, não é mesmo claro como isso poderia ser verdade, porque as línguas essas implementações são escritos em nem sequer têm ponteiros.)
abarnert
Eu acho que o uso de "ponteiro" em um sentido conceitual está ok (talvez com um aviso de que as implementações podem variar), especialmente se o objetivo é transmitir comportamento.
Levon
@abarnert Quando as pessoas dizem Python, querem dizer CPython, não outras implementações raramente usadas. Assim como quando as pessoas dizem lenços de papel, elas significam tecido facial. Jogar o jogo semântico nesses comentários é realmente desnecessário. Quanto ao que ele escreveu, o comportamento do que ele descreveu está errado?
swade 17/07/19
10

Você pode usar id(name)para verificar se dois nomes representam o mesmo objeto:

>>> a = b = c = [0, 3, 5]
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488

Listas são mutáveis; isso significa que você pode alterar o valor no local sem criar um novo objeto. No entanto, depende de como você altera o valor:

>>> a[0] = 1
>>> print(id(a), id(b), id(c))
46268488 46268488 46268488
>>> print(a, b, c)
[1, 3, 5] [1, 3, 5] [1, 3, 5]

Se você atribuir uma nova lista para a, em seguida, seu id vai mudar, por isso não vai afetar be cde valores:

>>> a = [1, 8, 5]
>>> print(id(a), id(b), id(c))
139423880 46268488 46268488
>>> print(a, b, c)
[1, 8, 5] [1, 3, 5] [1, 3, 5]

Os números inteiros são imutáveis, portanto você não pode alterar o valor sem criar um novo objeto:

>>> x = y = z = 1
>>> print(id(x), id(y), id(z))
507081216 507081216 507081216
>>> x = 2
>>> print(id(x), id(y), id(z))
507081248 507081216 507081216
>>> print(x, y, z)
2 1 1
jurgenreza
fonte
1
idnão é necessariamente um local de memória. Como dizem os documentos , isso retorna a "identidade ... um número inteiro ... que é garantido como único e constante para esse objeto durante sua vida útil". O CPython usa o endereço de memória como o id, mas outras implementações do Python podem não. PyPy, por exemplo, não. E dizer "dois vars apontam para o mesmo local de memória" é enganoso para quem entende o estilo C. "Dois nomes para o mesmo objeto" é mais preciso e menos enganoso.
Abarnert
@abarnert obrigado pelo esclarecimento, atualizei a resposta.
Jorgenreza # 03/13
7

no seu primeiro exemplo, a = b = c = [1, 2, 3]você está realmente dizendo:

 'a' is the same as 'b', is the same as 'c' and they are all [1, 2, 3]

Se você deseja definir 'a' igual a 1, 'b' igual a '2' e 'c' igual a 3, tente o seguinte:

a, b, c = [1, 2, 3]

print(a)
--> 1
print(b)
--> 2
print(c)
--> 3

Espero que isto ajude!

Nick Burns
fonte
4

Simplificando, no primeiro caso, você está atribuindo vários nomes a a list. Apenas uma cópia da lista é criada na memória e todos os nomes se referem a esse local. Portanto, alterar a lista usando qualquer um dos nomes modificará a lista na memória.

No segundo caso, várias cópias do mesmo valor são criadas na memória. Portanto, cada cópia é independente uma da outra.

Vikas
fonte
3

O que você precisa é este:

a, b, c = [0,3,5] # Unpack the list, now a, b, and c are ints
a = 1             # `a` did equal 0, not [0,3,5]
print(a)
print(b)
print(c)
pydsigner
fonte
3

O código que faz o que eu preciso poderia ser este:

# test

aux=[[0 for n in range(3)] for i in range(4)]
print('aux:',aux)

# initialization

a,b,c,d=[[0 for n in range(3)] for i in range(4)]

# changing values

a[0]=1
d[2]=5
print('a:',a)
print('b:',b)
print('c:',c)
print('d:',d)

Resultado:

('aux:', [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]])
('a:', [1, 0, 0])
('b:', [0, 0, 0])
('c:', [0, 0, 0])
('d:', [0, 0, 5])
Marco
fonte