Dictionary mit Platzhaltern durchsuchen

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.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Wie kommst du denn darauf, dass der Prozess angehalten werden würde? Vielleicht mal der Vergleich etwas im Detail:

Mittels der LC erstellst du eine Liste mit den Elementen x1, ..., xn und speicherst alle Elemente zunächst in einer Liste ab. Das ist der erste Durchlauf. Anschließend, um bei deinem Beispiel zu bleiben, iterierst du mittels der for-Schleife über alle Elemente der Liste und verarbeitest diese (Elemente). Wer hätte es gedacht, dass ist der zweite Durchlauf ^^

Bei der Generator-Variante erzeugst du zunächst nichts, abgesehen vom Generator. Nun iterierst du mittels der for-Schleife durch den Generator und erst wenn ein neuer Schritt benötigt wird, so wird dieser berechnet und anschließend sofort verarbeitet. Insgesamt hast du daher nur einen Schleifendurchlauf. Außerdem kommt hinzu, dass du immer noch ein Element halten musst und nicht alle Elemente der potentiellen Liste.

Bis dann,
Sebastian
Das Leben ist wie ein Tennisball.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

EyDu hat geschrieben:Wie kommst du denn darauf, dass der Prozess angehalten werden würde?
Mit "Prozess" hab' ich mich vielleicht unklar ausgedrückt. Natürlich wird nicht der Prozess im Sinne des Gesamten angehalten. Jedenfalls bleibt das Ding halt stehen... ;-)

Die Python Reference schreibt dazu:
At that time, the execution proceeds to the first yield expression, where it is suspended again, returning the value of expression_list to generator’s caller. By suspended we mean that all local state is retained, including the current bindings of local variables, the instruction pointer, and the internal evaluation stack. When the execution is resumed by calling one of the generator’s methods, the function can proceed exactly as if the yield expression was just another external call.
EyDu hat geschrieben:... wenn ein neuer Schritt benötigt wird, so wird dieser berechnet ...
1. Durchlauf
EyDu hat geschrieben:... und anschließend sofort verarbeitet.
Ist "sofort" so schnell, dass man es nicht mehr als 2. Durchlauf zählen kann...? ;-)
EyDu hat geschrieben:Insgesamt hast du daher nur einen Schleifendurchlauf.
Hmm... irgendwie ja auch egal, aber es sind doch 2...
Xynon1 hat geschrieben:Was den Vorteil betrifft, sollte dir klar sein - Geschwindigkeit und weniger Speicherverbrauch.
UiUiUi... da hüpft mein performancesüchtiges Herz...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Bei der 4-Element Liste die im Beispiel war würdest du wohl erst nach 10^6 Durchläufen einen Unterschied von eventuell 2 Sekunden haben. :D Aber wenn es eine längere Liste ist, wirst du heilfroh sein.

Und EyDu hat schon recht, es ist nur ein Schleifendurchlauf, aber zwei Schleifen, bei der Liste wären es 2 Schleifendurchläufe.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

sowas halt ..

Code: Alles auswählen

class Counter:
    cnt = 0
    def __init__(self):
        Counter.cnt = Counter.cnt+1
        print "count",Counter.cnt

counters = [Counter() for i in range(5)]
i = 0
for count in counters:
    i = i+1
    print "2nd_count",i

print "zeasur\n\n"

Counter.cnt = 0
mygenerator = (Counter() for x in range(5))
i = 0
for count in mygenerator:
    i = i+1
    print "2nd_count",i, "\n"

Code: Alles auswählen

count 1
count 2
count 3
count 4
count 5
2nd_count 1
2nd_count 2
2nd_count 3
2nd_count 4
2nd_count 5
zeasur


count 1
2nd_count 1 

count 2
2nd_count 2 

count 3
2nd_count 3 

count 4
2nd_count 4 

count 5
2nd_count 5 
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Xynon1:
Ich meinte das nicht ironisch. Ich finde performante Lösungen einfach sexy. Auch wenn keine "gefühlten" Geschwindigkeitsvorteile da sind.... :)
Xynon1 hat geschrieben:Und EyDu hat schon recht, es ist nur ein Schleifendurchlauf, aber zwei Schleifen, bei der Liste wären es 2 Schleifendurchläufe.
Tut mir leid, aber das will einfach nicht in meinen verkalkten Schädel rein! Wenn 2 Schleifen da sind, dann werden diese 2 Schleifen auch durchlaufen. Neben all dem für und wider LC vs. Generator unterscheiden sich beide im Abarbeiten ihrer inneren Anweisungen nicht.
Das Beispiel von lordnaikon finde ich da doch sehr anschaulich: Sowohl die LC wie auch der Generator tun ein und dasselbe. Wenn auch in unterschiedlicher Art.
Das ganze natürlich immer vor dem Hintergrund, dass die Bedingung(en) zum Beenden der Schleif(en) erfüllt sind, will heißen, dass die LC mit ihrer Liste fertig ist und der Generator "verbraucht" ist.

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@mutetella
Hatte ich auch nicht ironisch aufgefaßt.
Immer noch nicht kapiert ? :shock: Hier nochmal ein kleines Beispiel, obwohl lordnaikon das schon sehr deutlich gemacht hat.

Code: Alles auswählen

def list(*numbers):
   new_numbers = []
   for number in numbers:
      new_numbers.append(number)
   print(new_numbers)
   return new_numbers

def gen(*numbers):
   for number in numbers:
      print(number)
      yield number
Beispiele dienen nur der Veranschaulichung.
So ungefähr könnte das aussehen, nun gib mal folgendes ein:

Code: Alles auswählen

>>> list(1,2,3,4)
[1, 2, 3, 4]
[1, 2, 3, 4]
>>> gen(1,2,3,4)
<generator object gen at 0x01740238>
Wie du siehst, macht der Generator noch nichts, außer ein Generator-Objekt zu erzeugen. Die Liste hingegen, wird schon gebaut, und ist nach Aufruf der Funktion fertig, beim Generator wurd der Inhalt von "numbers" noch nicht einmal beachtet. Weiter geht es mit dem Iterieren:

Code: Alles auswählen

>>> for i in list(1,2,3,4):
	print i
[1, 2, 3, 4]
1
2
3
4

Hier sieht man, wärend die Liste zu Beginn erstellt wurde, also die innere Funktionsschleife bereits abgearbeitet wurde, nun darüber iteriert wird. --> Zwei Schleifendurchläufe.

Code: Alles auswählen

>>> for i in gen(1,2,3,4):
	print i
1
1
2
2
3
3
4
4
Hingegen der Generator bisher noch nichts gemacht hat und nun wird erst über die Äußere Schleife Daten "angefordert", da die innere Schleife auch noch keine "echte" Daten hat wird diese nun einmal mit "next" aufgerufen und gibt nun den Wert an die äußere Schleife weiter.
So entsteht ein wechsel zwischen innerer und äußerer Schleife, wobei immer nur das nächste Element betrachtet wird.
Wohingegen die Liste alle Elemente der inneren Schleife sofort an die äußere weitergibt.

Mit anderen Worten, du betrachtest bei der Liste immer alle Elemente, bei dem Generator immer nur zwei Elemente.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Ich glaube inzwischen, dass ich mich nur sehr ungenügend ausgedrückt habe... :K

Dass sich die Arbeitsweise einer LC von der eines Generators wesentlich unterscheidet ist mir doch längst klar. Worauf ich seit ein paar Beiträgen herumreite ist die Tatsache, dass
mutetella hat geschrieben:... vor dem Hintergrund, dass die Bedingung(en) zum Beenden der Schleif(en) erfüllt sind, ...
am Ende, d. h. wenn die äußere Iteration über die von einer LC produzierten Liste oder über einen Generator durchlaufen ist, der Generator in seinem Inneren nicht weniger leisten musste als eine LC.

Irgendwo in diesem Thread wurde der Eindruck erweckt, ein Generator sei effizienter als eine LC. In seiner Arbeitsweise ist er das zweifelsfrei. Aber am Ende seiner Tage hat er nicht weniger arbeiten müssen, als das eine LC beim Erstellen einer Liste muss.

Darf ich nun diese Erkenntnis behalten :D oder bin ich immer noch auf dem Holzweg :( ?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
/me
User
Beiträge: 3556
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mutetella hat geschrieben:Irgendwo in diesem Thread wurde der Eindruck erweckt, ein Generator sei effizienter als eine LC. In seiner Arbeitsweise ist er das zweifelsfrei. Aber am Ende seiner Tage hat er nicht weniger arbeiten müssen, als das eine LC beim Erstellen einer Liste muss.
Ein Generator muss keine Liste füllen. Der Overhead fällt weg.
lordnaikon
User
Beiträge: 58
Registriert: Dienstag 9. Februar 2010, 13:41

oder bin ich immer noch auf dem Holzweg ?
.. nicht allzu Holzig :mrgreen:

für kleine "Listen" ist es egal (dort hat sogar LC nen kleinen vorsprung, hier kann wohl lokal ohne overhead besser auf dem kleinen array gearbeiten werden), wenn du aber größere Listen hast, macht der Generator sinn. Auch schon, weil du mit dem LC die Liste im RAM halten musst, das wird teuer, jede Operation darauf schiebt dann immer Berge an Daten hin und her; der Generator generiert sie erst wenn sie gebraucht werden. Insgesammt hast du aber wohl recht, abstrackt gesehen macht der eine nicht mehr wie der andere. Die Unterschiede liegen wohl in der Speicherverarbeitung usw.

Code: Alles auswählen

import timeit

class Counter:
    cnt = 0
    def __init__(self):
        Counter.cnt = Counter.cnt+1

def list(n):
    counters = [Counter() for i in xrange(n)]
    i = 0
    for count in counters:
        i = i+1

def gen(n):
    mygenerator = (Counter() for x in xrange(n))
    i = 0
    for count in mygenerator:
        i = i+1


t1 = timeit.Timer("list(10)", "from __main__ import list") 
t2 = timeit.Timer("gen(10)", "from __main__ import gen")

print "list: ", t1.timeit(number=100) 
print "gen : ", t2.timeit(number=100)

t1 = timeit.Timer("list(100000)", "from __main__ import list") 
t2 = timeit.Timer("gen(100000)", "from __main__ import gen")

print "list: ", t1.timeit(number=100) 
print "gen : ", t2.timeit(number=100)

Code: Alles auswählen

list:  0.00159063239869
gen :  0.00167169347287
list:  16.710744263
gen :  13.4021026937
Benutzeravatar
hendrikS
User
Beiträge: 420
Registriert: Mittwoch 24. Dezember 2008, 22:44
Wohnort: Leipzig

Xynon1 hat geschrieben:3. Eher eine Frage - wenn man über eine Dictionary, um den Schlüssel und den Wert zu bekommen, iteriert sollte man in Python 2.x nicht "iteritems" benutzen ?
Wenn man den Wert hinter dem Schlüssel im Algorithmus nicht ständig benötigt, empfiehlt sich weder items() noch iteritems() zu verwenden und einfach über des dict zu iterieren, weil performanter. Den Code von EyDu kurz modifiziert:

Code: Alles auswählen

def find(name):
     for key in telefonbuch:
         if name in key:
             return key, telefonbuch[key]
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wobei omnidan ja nach einer Lösung gesucht hat, die sowohl den Schlüssel wie auch den Schlüsselwert durchsucht.
EyDu's und Deine modifizierte Lösung durchsuchen aber nur den Schlüsselnamen.

Es müsste also

Code: Alles auswählen

def find(name):
     for key in telefonbuch:
         if name in key or name in telefonbuch[key]:
             return key, telefonbuch[key]
lauten.

Allerdings bin ich mir nicht sicher, ob diese Lösung dann noch performanter ist...

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Effizienter ist (bei Python 2.x wäre iteritems() noch besser):

Code: Alles auswählen

def find(name):
     for key, value in telefonbuch.items():
         if name in key or name in value:
             return key, value
Stefan
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wo liegt eigentlich der Unterschied zwischen

Python2.dict.items() und Python3.dict.items()
bzw.
Python2.dict.iteritems() und Python3.dict.iteritems()

:?:

In diesem Thread wurde hier und da darauf verwiesen, dass man mit Python 2 das 'eine', unter Python 3 aber lieber das 'andere' verwenden sollte... Warum?

mutetella


EDIT: Ok, hab's gerade selbst nachgelesen... sorry für meine Ungeduld.
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten