Estou tentando entender a abordagem do Python para o escopo variável. Neste exemplo, por que é f()
possível alterar o valor de x
, conforme percebido dentro main()
, mas não o valor de n
?
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
Resultado:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
Respostas:
Algumas respostas contêm a palavra "cópia" no contexto de uma chamada de função. Acho confuso.
Python não copiar objetos que você passa durante uma chamada de função que nunca .
Parâmetros de função são nomes . Quando você chama uma função, o Python vincula esses parâmetros a qualquer objeto que você passar (por meio de nomes no escopo do chamador).
Os objetos podem ser mutáveis (como listas) ou imutáveis (como números inteiros, seqüências de caracteres em Python). Objeto mutável que você pode alterar. Você não pode alterar um nome, apenas pode vinculá-lo a outro objeto.
Seu exemplo não é sobre escopos ou namespaces , é sobre nomeação, vinculação e mutabilidade de um objeto no Python.
Aqui estão boas fotos sobre a diferença entre variáveis em outros idiomas e nomes em Python .
fonte
def foo(x, l=None): l=l or []; l.append(x**2); return l[-1]
.x = []
inf()
não tem efeito na listax
na função principal. Atualizei o comentário para torná-lo mais específico.Você já tem várias respostas, e eu concordo amplamente com JF Sebastian, mas você pode achar isso útil como um atalho:
A qualquer momento
varname =
, você cria uma nova ligação de nome no escopo da função. Qualquer valor quevarname
foi vinculado antes é perdido dentro desse escopo .Sempre que vir que
varname.foo()
você está chamando um métodovarname
. O método pode alterar varname (por exemplolist.append
).varname
(ou melhor, o objeto quevarname
nomeia) pode existir em mais de um escopo e, como é o mesmo objeto, qualquer alteração será visível em todos os escopos.[observe que a
global
palavra - chave cria uma exceção para o primeiro caso]fonte
f
na verdade, não altera o valor dex
(que é sempre a mesma referência a uma instância de uma lista). Em vez disso, altera o conteúdo desta lista.Nos dois casos, uma cópia de uma referência é passada para a função. Dentro da função,
n
recebe um novo valor. Somente a referência dentro da função é modificada, não a fora dela.x
não recebe um novo valor: nem a referência dentro nem fora da função é modificada. Em vez disso,x
o valor de é modificado.Como tanto a
x
função interna quanto a externa se referem ao mesmo valor, as duas vêem a modificação. Por outro lado, on
interior da função e o exterior referem-se a diferentes valores após seremn
reatribuídos dentro da função.fonte
Vou renomear variáveis para reduzir a confusão. n -> nf ou nmain . x -> xf ou xmain :
Quando você chama a função f , o tempo de execução do Python faz uma cópia do xmain e a atribui ao xf , e da mesma forma atribui uma cópia do nmain para nf .
No caso de n , o valor que é copiado é 1.
No caso de x, o valor copiado não é a lista literal [0, 1, 2, 3] . É uma referência a essa lista. xf e xmain estão apontando para a mesma lista; portanto, quando você modifica o xf, também está modificando o xmain .
Se, no entanto, você escrever algo como:
você descobriria que xmain não mudou. Isso ocorre porque, na linha xf = ["foo", "bar"], você alterou xf para apontar para uma nova lista. Quaisquer alterações feitas nessa nova lista não terão efeitos na lista para a qual o xmain ainda aponta.
Espero que ajude. :-)
fonte
nf = 2
, onde o nomenf
é alterado para apontar2
. Os números são imutáveis, as listas são mutáveis.É porque uma lista é um objeto mutável. Você não está definindo x para o valor de [0,1,2,3], está definindo um rótulo para o objeto [0,1,2,3].
Você deve declarar sua função f () assim:
fonte
x = x + [4]
vez dex.append(4)
, também não verá alterações no chamador, embora a lista seja mutável. Tem a ver com se é realmente mutado.x += [4]
,x
será alterado, exatamente como acontece comx.append(4)
o chamador, para que o chamador veja a alteração.n é um int (imutável) e uma cópia é passada para a função, portanto, na função você está alterando a cópia.
X é uma lista (mutável) e uma cópia do ponteiro é passada para a função para que x.append (4) altere o conteúdo da lista. No entanto, você disse que x = [0,1,2,3,4] em sua função, não alteraria o conteúdo de x em main ().
fonte
Se as funções são reescritas com variáveis completamente diferentes e chamamos id , isso ilustra bem o ponto. Eu não entendi isso no começo e li o post do jfs com a grande explicação , então tentei entender / me convencer:
zex têm o mesmo ID. Apenas tags diferentes para a mesma estrutura subjacente que o artigo diz.
fonte
Python é uma linguagem pura de passagem por valor, se você pensar da maneira certa. Uma variável python armazena a localização de um objeto na memória. A variável Python não armazena o próprio objeto. Quando você passa uma variável para uma função, está passando uma cópia do endereço do objeto que está sendo apontado pela variável.
Contraste essas duas funções
Agora, quando você digita no shell
Compare isso com gosma.
No primeiro caso, passamos uma cópia do endereço de cow para foo e foo modificou o estado do objeto que ali reside. O objeto é modificado.
No segundo caso, você passa uma cópia do endereço da vaca para a gosma. Então goo passa a alterar essa cópia. Efeito: nenhum.
Eu chamo isso de princípio da casa rosa . Se você fizer uma cópia do seu endereço e pedir ao pintor para pintar a casa naquele endereço de rosa, você terminará com uma casa rosa. Se você der uma cópia do seu endereço ao pintor e pedir para ele mudar para um novo endereço, o endereço da sua casa não será alterado.
A explicação elimina muita confusão. Python passa o endereço que as variáveis armazenam por valor.
fonte
Python é copiar pelo valor de referência. Um objeto ocupa um campo na memória e uma referência é associada a esse objeto, mas ele próprio ocupa um campo na memória. E o nome / valor está associado a uma referência. Na função python, ele sempre copia o valor da referência; portanto, no seu código, n é copiado para ser um novo nome; quando você o atribui, ele possui um novo espaço na pilha de chamadas. Mas para a lista, o nome também foi copiado, mas se refere à mesma memória (já que você nunca atribui à lista um novo valor). Isso é uma mágica em python!
fonte
Meu entendimento geral é que qualquer variável de objeto (como uma lista ou um ditado, entre outras) pode ser modificada por meio de suas funções. O que eu acredito que você não é capaz de fazer é reatribuir o parâmetro - ou seja, atribuí-lo por referência dentro de uma função que pode ser chamada.
Isso é consistente com muitos outros idiomas.
Execute o seguinte script curto para ver como funciona:
fonte
Eu havia modificado minha resposta várias vezes e percebi que não precisava dizer nada, o python já havia se explicado.
Este diabo não é a referência / valor / mutável ou não / instância, espaço de nome ou variável / lista ou str, É A SINTAXE, SINAL DE IGUALDADE.
fonte