Unerwartete Listenverständnisverhalten in Python
-
03-07-2019 - |
Frage
Ich glaube, ich werde von einer Kombination aus verschachtelten Scoping -Regeln und Listenfindungen gebissen. Jeremy Hyltons Blog -Beitrag ist suggestiv über die Ursachen, aber ich verstehe die Implementierung von CPython nicht wirklich, um herauszufinden, wie ich mich umgehen kann.
Hier ist ein (überkompriziertes?) Beispiel. Wenn die Leute eine einfachere haben, die es demonstriert, würde ich es gerne hören. Das Problem: Die List -Verständnisse unter Verwendung von Next () sind mit dem Ergebnis aus der letzten Iteration gefüllt.
bearbeiten: Das Problem:
Was genau ist damit los und wie kann ich das beheben? Muss ich einen Standard für die Schleife verwenden? Klar in der Funktion wird die richtige Häufigkeit ausgeführt, aber die Liste der Liste Finale Wert anstelle des Ergebniss jeder Schleife.
Einige Hypothesen:
- Generatoren?
- Faule Füllung von Listenverständnissen?
Code
import itertools
def digit(n):
digit_list = [ (x,False) for x in xrange(1,n+1)]
digit_list[0] = (1,True)
return itertools.cycle ( digit_list)
>>> D = digit(5) >>> [D.next() for x in range(5)] ## This list comprehension works as expected [(1, True), (2, False), (3, False), (4, False), (5, False)]
class counter(object):
def __init__(self):
self.counter = [ digit(4) for ii in range(2) ]
self.totalcount=0
self.display = [0,] * 2
def next(self):
self.totalcount += 1
self.display[-1] = self.counter[-1].next()[0]
print self.totalcount, self.display
return self.display
def next2(self,*args):
self._cycle(1)
self.totalcount += 1
print self.totalcount, self.display
return self.display
def _cycle(self,digit):
d,first = self.counter[digit].next()
#print digit, d, first
#print self._display
self.display[digit] = d
if first and digit > 0:
self._cycle(digit-1)
C = counter()
[C.next() for x in range(5)]
[C.next2() for x in range(5)]
AUSGANG
In [44]: [C.next() for x in range(6)] 1 [0, 1] 2 [0, 2] 3 [0, 3] 4 [0, 4] 5 [0, 1] 6 [0, 2] Out[44]: [[0, 2], [0, 2], [0, 2], [0, 2], [0, 2], [0, 2]] In [45]: [C.next2() for x in range(6)] 7 [0, 3] 8 [0, 4] 9 [1, 1] 10 [1, 2] 11 [1, 3] 12 [1, 4] Out[45]: [[1, 4], [1, 4], [1, 4], [1, 4], [1, 4], [1, 4]] # this should be: [[0,3],[0,4]....[1,4]] or similar
Lösung
Das Problem ist das mit return self.display
Sie geben eine zurück Hinweis auf diese Liste (keine Kopie). Was Sie also haben, ist eine Liste, in der jedes Element ein Verweis auf sich selbst ist. Display. Schauen Sie sich die folgende an:
>>> a = [1,2]
>>> b = [a,a]
>>> b
[[1, 2], [1, 2]]
>>> a.append(3)
>>> b
[[1, 2, 3], [1, 2, 3]]
Sie möchten wahrscheinlich so etwas verwenden return self.display[:]
.
Andere Tipps
Wenn ich das ein bisschen wieder aufrefiziere?
def digit(n):
for i in itertools.count():
yield (i%n+1, not i%n)
Aber tatsächlich brauchen Sie das nicht, wenn Sie das Ganze als einfache Iterator implementieren:
def counter(digits, base):
counter = [0] * digits
def iterator():
for total in itertools.count(1):
for i in range(len(counter)):
counter[i] = (counter[i] + 1) % base
if counter[i]:
break
print total, list(reversed(counter))
yield list(reversed(counter))
return iterator()
c = counter(2, 4)
print list(itertools.islice(c, 10))
Wenn Sie den Druck loswerden möchten (Debugging, oder?), Gehen Sie mit einer Weile Schleife.
Dies löst auch Ihr anfängliches Problem, weil reversed
Gibt eine Kopie der Liste zurück.
Oh, und es basiert jetzt auf Null;)