Portée des générateurs Python récursives
Question
Salut à tous, je travaillais sur un générateur récursif pour créer les partitions entières fixes d'un certain nombre et j'étais confus par un problème de cadrage.
Le code est similaire à cet extrait.
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
Ma confusion est illustré ci-dessous.
>>> 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]]
Je peux obtenir la bonne réponse simplement en utilisant une copie du tableau (en passant par exemple dans l'appel à a[:]
récursive) mais je ne comprends toujours pas le comportement ci-dessus.
Pourquoi les déclarations d'impression et les valeurs de rendement différentes?
La solution
L'instruction print affiche la liste à ce moment précis dans le temps. Votre code modifie la liste que vous exécutez, donc au moment où vous examinez la liste à la fin, vous voyez alors sa valeur.
Vous pouvez observer cela en parcourant:
>>> 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]
Autres conseils
Je suppose que vous mutent le tableau, alors quand vous imprimez a une valeur particulière, alors la prochaine fois que vous imprimez a effectivement mis à jour la valeur, et ainsi de suite. A la fin, vous avez 5 références au même tableau, donc bien sûr, vous avez la même valeur 5 fois.
Les listes sont des objets mutables, si vous passez dans une liste, et le générateur effectue des opérations en place sur cette liste, puis enfin toutes les références à la liste pointera vers la même liste.
Les états d'impression et de rendement sont différents parce que vous ne disposez que d'une déclaration d'impression alors que vous avez 2 rendements. Essayez ceci:
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]]
Vous verrez que les derniers rendements sont vos réponses parce que vous avez de passage autour de la même liste au lieu de faire une copie.