Tente estes três problemas e verifique as respostas no final do artigo.
Dica : Os problemas têm algo em comum; portanto, retifique o primeiro problema ao passar para o segundo ou terceiro, será mais fácil para você.
Primeira tarefa
Existem várias variáveis:
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
O que será exibido ao imprimir
le s?
Segunda tarefa
Defina uma função simples:
def f(x, s=set()):
s.add(x)
print(s)
O que acontece se você ligar para:
>>f(7)
>>f(6, {4, 5})
>>f(2)
Terceira tarefa
Definimos duas funções simples:
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
O que obtemos depois de executar esses comandos?
>>f_inner = f()
>>print(f_inner(2))
>>g_inner = g()
>>print(g_inner(2))
Quão confiante você está em suas respostas? Vamos verificar o seu caso.
A solução para o primeiro problema
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
Por que a segunda lista responde a uma alteração em seu primeiro elemento
a.append(5)e a primeira lista ignora completamente a mesma alteração x+=5?
Solução do segundo problema
Vamos ver o que acontece:
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
Espere, não deveria ser o último resultado
{2}?
Solução do terceiro problema
O resultado será assim:
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
Por
g_inner(2)que ela não fez 3? Por f()que a função interna lembra o escopo externo, mas a função interna g()não lembra? Eles são quase idênticos!
Explicação
E se eu lhe dissesse que todos esses comportamentos estranhos têm a ver com a diferença entre objetos mutáveis e imutáveis no Python?
Objetos mutáveis, como listas, conjuntos ou dicionários, podem ser alterados no local. Objetos imutáveis, como valores numéricos e de sequência, as tuplas não podem ser modificadas; sua "mudança" levará à criação de novos objetos.
Primeira explicação da tarefa
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
Por ser
ximutável, a operação x+=5não altera o objeto original, mas cria um novo. Mas o primeiro elemento da lista ainda se refere ao objeto original, portanto, seu valor não muda.
Porque um objeto mutável, o comando
a.append(5)modifica o objeto original (em vez de criar um novo) e a lista s"vê" as alterações.
Explicação da segunda tarefa
def f(x, s=set()):
s.add(x)
print(s)
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
Tudo fica claro com os dois primeiros resultados: o primeiro valor é
7adicionado ao conjunto inicialmente vazio e resulta {7}; então o valor é 6adicionado ao conjunto {4, 5}e obtido {4, 5, 6}.
E então começam as esquisitices. O valor
2não é adicionado ao conjunto vazio, mas a {7}. Por quê? O valor inicial do parâmetro opcional é savaliado apenas uma vez: na primeira chamada, s será inicializado como um conjunto vazio. E, uma vez que é mutável, após a chamada, f(7)ela será alterada “no lugar”. A segunda chamada f(6, {4, 5})não afetará o parâmetro padrão: o aparelho o substitui {4, 5}, ou seja, é {4, 5}uma variável diferente. A terceira chamada f(2)usa a mesma variávelsque foi usado durante a primeira chamada, mas não é reinicializado como um conjunto vazio, mas seu valor anterior é obtido {7}.
Portanto, você não deve usar argumentos mutáveis como argumentos padrão. Nesse caso, a função precisa ser alterada:
def f(x, s=None):
if s is None:
s = set()
s.add(x)
print(s)
Explicação da terceira tarefa
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
Aqui estamos lidando com fechamentos: funções internas lembram como era o espaço para nome externo quando elas foram definidas. Ou pelo menos eles devem se lembrar, mas a segunda função faz a interface do poker e se comporta como se não tivesse ouvido falar de seu espaço de nomes externo.
Por que isso está acontecendo? Quando executamos
l.append(x), o objeto mutável criado quando a função é definida muda. Mas a variável lainda se refere ao endereço de memória antigo. No entanto, tentar alterar uma variável imutável na segunda função y += xresulta em y começando a se referir a um endereço de memória diferente: o y original será esquecido, o que resultará em um UnboundLocalError.
Conclusão
A diferença entre objetos mutáveis e imutáveis no Python é muito importante. Evite o comportamento estranho descrito neste artigo. Especialmente:
- .
- - .