2017-12-13 5 views
2

fand ich mich unter ‚Auswertezeit Diskrepanz‘ von this list heute in die Gotcha ausgeführt wird, und eine harte Zeit habe um daran zu arbeiten.Arbeiten um Diskrepanzauswertung Zeit in Generatoren

Als kurze Demonstration meines Problems, mache ich unendlich Generatoren, die jede n te Zahl überspringen, mit n von [2..5] gehen:

from itertools import count 

skip_lists = [] 
for idx in range(2, 5): 
    # skip every 2nd, 3rd, 4th.. number 
    skip_lists.append(x for x in count() if (x % idx) != 0) 

# print first 10 numbers of every skip_list 
for skip_list in skip_lists: 
    for _, num in zip(range(10), skip_list): 
     print("{}, ".format(num), end="") 
    print() 

Erwartete Ausgabe:

1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 
1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

tatsächliche Ausgang:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

Sobald ich diese große Eigenschaft erinnerte, habe ich versucht, es zu „lösen“, indem die if Klausel Variable auf eine konstante Bindung, die Teil des skip_list wäre:

from itertools import count 

skip_lists = [] 
for idx in range(2, 5): 
    # bind the skip distance 
    skip_lists.append([idx]) 
    # same as in the first try, but use bound value instead of 'idx' 
    skip_lists[-1].append(x for x in count() if (x % skip_lists[-1][0]) != 0) 

# print first 10 numbers of every skip_list 
for skip_list in (entry[1] for entry in skip_lists): 
    for _, num in zip(range(10), skip_list): 
     print("{}, ".format(num), end="") 
    print() 

Aber auch hier:

1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 
1, 2, 3, 5, 6, 7, 9, 10, 11, 13, 

Neben einer tatsächlichen Lösung, ich würde gerne auch mein Hack erfahren, warum nicht funktioniert hat.

Antwort

3

Der Wert idx wird nie aufblickte, bis Sie auf den Generatoren Iterieren starten (Generatoren sind träge ausgewertet), an welchem ​​Punkt idx = 4 der neueste iteratee Wert, was in dem Modul Umfang vorhanden ist.

Sie können jeden angehängten Generator in idx stateful machen, indem Sie idx an eine Funktion übergeben und den Wert aus dem Funktionsumfang bei jeder Auswertezeit des Generators ablesen. Dies nutzt die Tatsache aus, dass die iterable Quelle eines Generator Expression wird im Gen beurteilt. exp die Erstellungszeit, so wird die Funktion bei jeder Iteration der Schleife genannt, und idx ist sicher verstaut im Funktionsbereich:

from itertools import count 

skip_lists = [] 

def skip_count(skip): 
    return (x for x in count() if (x % skip) != 0) 


for idx in range(2, 5): 
    # skip every 2nd, 3rd, 4th.. number 
    skip_lists.append(skip_count(idx)) 

Illustration des Generators bei Gen iterable Quelle Auswertung des Ausdrucks. exp Schöpfung:

>>> (i for i in 5) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: 'int' object is not iterable 

Ihr Fall ist ein bisschen schwieriger, da die Ausschlüsse tatsächlich getan werden in einem Filter, die nicht auf der Erstellungszeit des exp gen ausgewertet:

>>> (i for i in range(2) if i in 5) 
<generator object <genexpr> at 0x109a0da50> 

Je mehr Grund, warum die für Schleife und alle Filter in einen Rahmen bewegt werden müssen, die idx speichert; nicht nur der Filter.


Auf einer anderen Note, Sie itertools.islice statt der ineffiziente Logik verwenden können Sie eine Scheibe der Generator Ausdrücke drucken verwenden:

from itertools import islice 

for skip_list in skip_lists: 
    for num in islice(skip_list, 10): 
     print("{}, ".format(num), end="") 
    print() 
+0

Große Antwort, mein Code funktioniert jetzt =) Aber ich fürchte, ich verstehe immer noch nicht, warum 'idx' bei 'skip_lists.append ([idx])' gespeichert wird und der Zugriff auf 'idx' nicht funktioniert. Dieser Wert ändert sich nicht zur letzten Iteration, 'print (skip_list)' ergibt '[2, ], [3, ], [4, ]] 'für das zweite Beispiel immerhin. – Arne

+0

Das Codeobjekt des Generierungsausdrucks liest zum Zeitpunkt der Auswertung den Namen "idx" ein, und zwar nach dem Abschluss der Schleife. Mehr wie das Drucken von 'idx' nach dieser Schleife. Der Name 'idx' verweist an dieser Stelle auf den Int 4. –

+0

Das ist schrecklich. Nicht einmal 'skip_lists.append ([lambda x = idx: x])' 'funktioniert, um eine lokal begrenzte Referenz zu erstellen. Danke, dass du mir gezeigt hast, wie du das lösen kannst. – Arne