Âmbito de Geradores de Python recursiva
Pergunta
Olá a todos, eu estava trabalhando em um gerador de recursiva para criar o fixo inteiro partições de um número e eu estava confuso por um problema de delimitação do âmbito.
O código é semelhante a esse trecho.
def testGen(a,n):
if n <= 1:
print('yield', a)
yield a
else:
for i in range(2):
a[i] += n
for j in testGen(a,n-i-1):
yield j
A minha confusão é ilustrado abaixo.
>>> list(testGen([1,2],4))
yield [10, 2]
yield [10, 4]
yield [10, 7]
yield [12, 11]
yield [12, 13]
[[12, 13], [12, 13], [12, 13], [12, 13], [12, 13]]
Eu posso obter a resposta certa simplesmente usando uma cópia da matriz (por exemplo, passando em a[:]
para a chamada recursiva), mas eu ainda não entendo o comportamento acima.
Por que são as declarações de impressão e valores de rendimento diferentes?
Solução
A declaração de impressão exibe a lista nesse ponto específico no tempo. Seu código altera a lista como você executá-lo, por isso, o tempo que você examinar a lista no final, você vê o seu valor depois.
Você pode observar esse, percorrendo:
>>> g = testGen([1,2],4)
>>> g.next()
('yield', [10, 2]) # note brackets in print statement because I'm on python 2.5
[10, 2]
>>> g.next()
('yield', [10, 4])
[10, 4]
>>> g.next()
('yield', [10, 7])
[10, 7]
>>> g.next()
('yield', [12, 11])
[12, 11]
>>> g.next()
('yield', [12, 13])
[12, 13]
Outras dicas
Eu acho que você está transformando a matriz, então quando você imprimi-lo tem um valor particular, então a próxima vez que você imprimi-lo tem realmente atualizou o valor, e assim por diante. No final, você tem 5 referências para a mesma matriz, então é claro que você tem o mesmo valor de 5 vezes.
As listas são objetos mutáveis, se você passar em uma lista, e as executa gerador operações no local nessa lista, então, finalmente, todas as referências à lista irá apontar para a mesma lista.
O impressão e rendimento declarações são diferentes, porque você só tem uma declaração de impressão enquanto você tem 2 rendimentos. Tente isto:
def testGen(a,n):
if n <= 1:
print('yield', a)
yield a
else:
for i in range(2):
a[i] += n
for j in testGen(a,n-i-1):
print('yield', j)
yield j
>>> list(testGen([1,2],4))
('yield', [10, 2])
('yield', [10, 2])
('yield', [10, 2])
('yield', [10, 2])
('yield', [10, 4])
('yield', [10, 4])
('yield', [10, 4])
('yield', [10, 4])
('yield', [10, 7])
('yield', [10, 7])
('yield', [10, 7])
('yield', [12, 11])
('yield', [12, 11])
('yield', [12, 11])
('yield', [12, 13])
('yield', [12, 13])
('yield', [12, 13])
[[12, 13], [12, 13], [12, 13], [12, 13], [12, 13]]
Você vai ver que os últimos rendimentos são as suas respostas, porque você foi passando ao redor da mesma lista em vez de fazer uma cópia.