Never ending Iteration ;-)

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

Hallo Forum,

im Zuge einer Projektvereinigung ist die Frage aufgekommen ob es
möglich ist, über eine liste zu iterieren die während der iteration verändert
werden kann. (gem. Brainstorming)

Kann so etwas in python ohne viel Aufwand realisiert werden?

Nachtrag: Wenn das Ende der Liste erreicht wurde, soll mit dem ersten
item fortgefahren werden.

VG., chris
be or not to be
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Öhm, mache eine Kopie und iteriere da drüber. Bist du durch, das Spiel von vorne. Über sich verändernde Listen iteriert Python nicht, sondern gibt dann eine Exception aus (und das ist gut so...).
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

hallo mkesper,

hatte ich auch schon gedacht. aber das produziert bestimmt ordentlich overhead und den kann ich leider nicht gebrauchen.
so weit ich weiß wurde, das auch schon mal in python gefixt. habs mal irgendwo auf nen maillist gelesen.
und das ist gut so...
wozu? es muss do auch möglich sein auch dann zu iterieren wenn
die liste verändert werden kann.

vg, chris
be or not to be
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

In welcher Form soll sie denn verändert werden? Sollen Elemente ausgetauscht oder angehängt werden?

Schon mal an eine Kombination aus 'while' und Indizes gedacht?
CM
User
Beiträge: 2464
Registriert: Sonntag 29. August 2004, 19:47
Kontaktdaten:

maxwell hat geschrieben:wozu? es muss do auch möglich sein auch dann zu iterieren wenn
die liste verändert werden kann.
Weil bei einer sich veränderden Liste die Elemente über die iteriert wird nicht mehr eindeutig sind: Was ist das *nächste* Element, wenn man Elemente eingefügt / herausgenommen hat? Das, das am Anfang dem gerade aktuellen folgte? Oder dasjenige, das *jetzt* dem gerade aktuellen folgen wird? Wie soll der Interpreter das entscheiden?
Nein, besser ist es an der Stelle Ausnahmen zu werfen und so einen Haufen potentieller Bugs zu vermeiden.

Ggf. sind aber Listcomprehensions genau was Du suchst:

Code: Alles auswählen

l = [foo(item) for item in l]
Aber vielleicht magst Du uns erzählen, was *eigentlich* das Problem ist? Erfahrungsgemäß hilft es wenig, wenn Leute fragen "wie mache ich x am besten in Python?", wenn nicht klar ist, was eigentlich erreicht werden soll. Ggf. gibst Du uns ein Minimalbeispiel von dem, was Du machen willst.

HTH
Christian
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

hallo,

folgendes soll erreicht werden.

Es soll über eine Liste mit IO-Buffer Elementen iteriert werden.
Jeder Buffer gleicht von seinem Verhalten einer Liste + Condition
im "wait" Fall. Es soll aus jedem Buffer genau ein Element entnommen
und in eine Queue geschrieben werden an deren Ende Prozesse auf den Inhalt warten. Wenn kein Element im Buffer vorhanden ist, soll es beim
nächsten Buffer versucht werden.

Synchron gesehen, könnte man ja auch nur alles hintereinander in die
queue schreiben. Aber ich möchte es gerne asynchron hinbiegen, sodass jobs auch voneinander abhängig sein können. a la:

Prozess 1 -> Einlesen [100 Elemente]
Prozess 2 -> Auswerten [100 Elemente]

Im Prinzip geht es um eine gerechte Aufteilung der Jobs auf die
Prozesse. Bsp. Prozess 1 hat 40 Elemente bearbeitet und eigentlich könnten diese bereits von Prozess 2 abgearbeitet werden, sodass
Resultate existieren. Im synchronen Fall werden alle Elemente von
Prozess 1 bearbeitet bevor Prozess 2 die ersten Elemente erreicht.

vg. chris

Nachtrag:

Ich habe das wie folgt gelöst:

Alle jobs inkl. parameter werden als tuple in eine deque gestellt.
Der Verteiler Thread ließt aus der liste am hinteren ende via pop
und entnimmt ein Element. Anschließend stellt er das tuple wieder
vorne via appendleft() an.
Zuletzt geändert von maxwell am Montag 23. November 2009, 12:55, insgesamt 1-mal geändert.
be or not to be
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Und an welcher Stelle müsstest du da jetzt über eine Liste iterieren und diese gleichzeitig verändern?! Über eine Queue iteriert man ja nicht, sondern holt sich ein Element nach dem nächsten, bis die Q leer ist.

Edit: Du kannst die Buffer ja genauso als Queue implementieren.

Edit2: Das mit dem vorne anstellen ist sehr seltsam, warum nimmst du nicht einfach 2 Queues für jeden Prozess einen?!

btw: Man kann den Interpreter überlisten, dies ist jedoch AUF KEINEN FALL zu empfehlen.

Code: Alles auswählen

>>> l = [1,2,3]
>>> inserted = False
>>> for val in iter(l):
...     if val == 1 and not inserted:
...         l.insert(0, 0)
...         inserted = True
...
>>> l
[0, 1, 2, 3]
>>> for pos, value in enumerate(iter(l)):
...     if value == 1:
...         l.insert(pos, 0)
...
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
MemoryError
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

Und an welcher Stelle müsstest du da jetzt über eine Liste iterieren und diese gleichzeitig verändern
In genau dem Moment wenn der mainthread neue Buffer in die
Liste einträgt.

Ich habe da wie folgt gelöst:

Code: Alles auswählen


a=(ta, iter([i for i in range(8)]), 3)
b=(tb, iter([i for i in range(5)]), 1)
c=(tc, iter([i for i in range(2)]), 2)

x=deque()
x.append(a)
x.append(b)
x.append(c)

while 1:
    try:
        z=x.pop()
        a=_islice(z[1],z[2])
        a=tuple(a)
        print (z[0],a)     
        if not a:
            continue
        x.appendleft(z)
    except Exception, e:
        break

be or not to be
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Dein Code ist sehr seltsam und nichtssagend (u.a. schlechte Variablennamen, was macht ``_isslice``). Kann gerade keine Parallelen zu deiner Beschreibung und deinem Code ziehen. Kannst du das mal genauer mit Code erläutern.

Außerdem kann man auch einen Generator-Ausdruck verwenden, statt ``iter(ListCompr)``, wobei xrange natürlich selbst schon einen Iterator zurückgibt:

Code: Alles auswählen

iter([i for i in xrange(3)]) 
# äquivalent
(i for i in xrange(3))
# äqu.
xrange(3)
Edit: Obwohl ich deinen Code nicht wirklich verstanden habe, habe ich das Gefühl, dass dein Problem mit 2 Queues gelöst wäre.

Edit2: Wo ist eigentlich die Iteration in deinem Codebeispiel? Diese Frage ist immer noch nicht beantwortet.

Edit3: Mir ist nochwas eingefallen. Du redest hier im Kontext von Threads und Prozessen, als wären sie das selbe. Ich hoffe dir ist bewusst, dass das nicht so ist. In diesem Zusammenhang sollte noch was gesagt werden: Eine Multithreading-Anwendung mit Python bringt *keine* Performance-Vorteile. Im Gegenteil hat man durch den GIL sogar noch Overhead. Um mit Python und einem Multicore Performance-Vorteile zu erziehlen, gibt es das Modul ``multiprocessing``. In dem Modul gibt es z.B. auch schon die Klasse ``Queue``. Schau dir mal die Beispiele in der Doku an, du wirst sehen, dass dieses Modul normal alle Funktionalitäten bereitstellt, die du benötigst, ohne auf solche seltsamen Konstrukte wie beschrieben und gezeigt zurückgreifen zu müssen.
Zuletzt geändert von ms4py am Montag 23. November 2009, 13:38, insgesamt 1-mal geändert.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@ ice2k3

der code ist nur beispiel und wird niemals produktiv. zumindest in der form.

Code: Alles auswählen

from itertools import isslice as _islice
ja du hast recht man kann auch xrange nehmen. aber hier unbrauchbar -> siehe islice. Es soll außerdem auch nur eine liste simuliert werden.

Code: Alles auswählen

def target1(): pass
def target2(): pass
def target3(): pass

pendings = deque()

# tuple structure:
# [target_function], [list_iterator], [chunksize]
# target function -> aufgerufene funktion durch den prozess
# list_iterator -> erzeugter iterator über die argumenten liste
# chunksize -> length der an das target zu übergebene argumentenliste

pendings.append((target1, iter([i for i in range(10)]), 2)) 
pendings.append((target2, iter([i for i in range(10)]), 1))
pendings.append((target3, iter([i for i in range(10)]), 3))

def distribute(pendings):
    while 1:
        try:
            # lade job aus queue
            sample = pendings.pop()
            # schneide aus der liste anzahl der argumente
            args = islice(sample[1], sample[2])
            # verpacke es als tuple 
            args = tuple(args) 
            # wenn tuple leer dann gehe zum nächsten job
            if not args:
                continue
            # halte hier und gebe das tuple und das target zurück
            yield (sample[0], args, sample[2])
            # anschließend stelle den job hinten an
            pendings.appendleft(sample)        
        except Exception, e:
            # ok ende erreicht!!!
            break

for i in distribute(pendings):
    print i
    # hier dann rein in die pipe queue 

Sieht mans nun besser?
Obwohl ich deinen Code nicht wirklich verstanden habe, habe ich das Gefühl, dass dein Problem mit 2 Queues gelöst wäre.
Wie ???
be or not to be
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Siehe mein Edit3 von oben!

Außerdem ist mir nicht klar, warum du das Ausschneiden erst innerhalb der Schleife machst und nicht schon beim Hinzufügen.

(Mir ist immer noch nicht klar, wo die Iteration sein soll, die gleichzeitig über eine Liste iteriert und diese verändert, allerdings ist das mit einer Lösung mit ``multiprocessing`` auch nicht wichtig...)

Edit: Außerdem sollte deine try..except wirklich nur die Exception abfangen, dass die Queue leer ist. Man sollte nie allgemein ``Exception`` abfangen, und vor allem nicht über so einen langen Codeblock. Es gibt ja auch noch try..except..else

Edit2: ``class multiprocessing.JoinableQueue`` wär vielleicht auch sinnvoll einzusetzen.
maxwell
User
Beiträge: 69
Registriert: Samstag 11. Juli 2009, 15:36
Wohnort: am Fernsehturm in B.

@ ice2k3

Erstmal Danke für Deine Bemühungen. Ich muss erstmal nen Gang zurückfahren. Folgendes:

Wir haben - siehe oben - bereits den Kontext gewechselt. D.h. ich habe oben bereits geschrieben, dass ich folgende Lösung habe. Diese hat mit
der Frage an sich eher wenig gemein, denn diese Lösung basiert auf einem
ganz anderen Ansatz. Sorry wenn wir uns hier missverstanden habe.

Nun zu Deinen Aussagen:
Du redest hier im Kontext von Threads und Prozessen, als wären sie das selbe. Ich hoffe dir ist bewusst, dass das nicht so ist.
Ja, ganz sicher.
In diesem Zusammenhang sollte noch was gesagt werden: Eine Multithreading-Anwendung mit Python bringt *keine*
Performance-Vorteile.
Nein nicht immer wenn Du auf Netztwerkebene arbeitest dann
gibt so etwas wie Latenzen.
Im Gegenteil hat man durch den GIL sogar noch Overhead.
Welche Auswirkungen der GIL (CPython) hat ist mir nicht neu.
Um mit Python und einem Multicore Performance-Vorteile zu erziehlen, gibt es das Modul ``multiprocessing``. In dem Modul gibt es z.B. auch schon die Klasse ``Queue``.
Da liegt genau der Haken, da synchron. Sieh dir bitte mal den Lader
an. siehe: _handle_tasks()
seltsamen Konstrukte wie beschrieben und gezeigt zurückgreifen zu müssen.
Diese seltsamen Konstrukte werden in 'multiprocessing' auch benutzt.
Es gibt in python viele verschieden Ansätze für ein und das selbe Problem.
Das ist ja auch der Vorteil. Ich glaube nicht, dass mein Code in irgendeiner Form komisch ist. Da gegenüber ist eine Geisterfahrt auf der A2 sicher aufregender.

Sorry für die Missverständnisse. ;-)

VG., Chris
be or not to be
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

maxwell hat geschrieben:Da liegt genau der Haken, da synchron. Sieh dir bitte mal den Lader
an. siehe: _handle_tasks()
"Synchron" bedeutet in diesem Zusammenhang, dass die Queue Thread- und Prozesssicher ist. Genau das, was du benötigst! Du kannst trotzdem parallel mit Objekten, die du vom Queue geholt hast, operieren.
Lader ??
_handle_tasks() ??

Sicher gibt es viele Möglichkeiten um ein Ziel zu erreichen, aber einige davon sind halt einfacher, übersichtlicher und wartbarer. Du haust ja auch nicht mit einer Axt ein Stück vom Brotlaib, nur weil man das machen könnte. ;)

Edit: Mir ist immer noch nicht klar, was du genau unter "asynchron" verstehst. Ich denke, da steckt auch der Knackpunkt. Bitte mal ein kurzes Codebeispiel. Was sind die verschiedenen Prozesse bzw. Funktionen und von was hängen diese ab und wie sollen diese "asynchron" aufgerufen werden.
Antworten