2016-05-16 16 views
2

Gibt es eine eingebaute in/empfohlen, den Dekorateur zu ersetzen:Gibt es einen eingebauten Weg (z. B. einen Dekorator), um einen Python-Generator zur Definition zu instantiieren?

def generator(f): 
    return f() 

im folgende Beispiel?

from random import randint 

@generator 
def mygenerator(): 
    while True: 
     yield randint(0, 9) 

for i in mygenerator: 
    print i 

... wie will ich nicht schreiben:

for i in mygenerator(): 
    print i 

Dies ist ein vereinfachtes Beispiel. Im tatsächlichen Anwendungsfall gibt es keinen Grund/Grund, zwei Instanzen von mygenerator zu haben, daher möchte ich die Instanz sofort erstellen. Vorzugsweise so, dass keine andere Instanz erstellt werden kann.

+1

Gibt es irgendeinen Grund/Grund * nicht *? Mit solch einem einfachen Beispiel könnte man es wahrscheinlich mit 'itertools' und nicht mit einer Funktion machen. – jonrsharpe

+0

Ja. Ich habe versucht, in meinem letzten Absatz klarzustellen, dass dies alles zu stark vereinfacht ist ... Ich nehme an, ich möchte komplexe Dinge in der Definition von "Mygenerator" machen. Und vermeiden Sie, dass zwei Instanzen erstellt werden. – NichtJens

+1

@azrael: Wenn Sie möchten, dass diese "komplexen Dinge" zur Definitionszeit passieren, wird das, wonach Sie fragen, diesen Effekt nicht haben. Keiner der Körper der Generatorfunktion wird bis zum ersten "nächsten" ausgeführt. Wenn Sie zwei Iteratoren vermeiden möchten, sollten Sie nicht zwei Iteratoren erstellen, statt die Funktion durch ihren Rückgabewert zu ersetzen. – user2357112

Antwort

2

Die integrierte Möglichkeit, es so zu tun ist:

def mygenerator(): 
    while True: 
     yield randint(0, 9) 

mygenerator = mygenerator() 

Das zumindest hat die Tugend klar zu sein, so müssen Sie nicht sehen, was @generator tut.

+0

Ah richtig! Das ist in der Tat, was ich tun sollte. Ich hatte nicht daran gedacht, die Funktion einfach mit ihrem Ergebnis zu überschreiben (obwohl das der Dekorateur tut) ... – NichtJens

2

Generatoren erzeugt werden können, die () Syntax - also

mygenerator = (randint(0, 9) for i in range(0, 100)) 

aber Ihr Problem, entnehme ich, ist, dass der Generator unendlich ist - Sie dies versuchen könnte:

import itertools 
from random import randint 

mygenerator = (randint(0, 9) for i in itertools.count()) 

this answer Siehe zusätzliche Details

+0

Die Endlosschleife ist nicht unbedingt das Problem. Wie ich versucht habe zu sagen, ist dies ein vereinfachtes Beispiel. Nehme an, ich möchte komplexere Dinge in 'mygenerator' machen, die nicht als Generatorausdruck ausgeführt werden können. Aber Sie haben sicherlich Recht: Diese können nicht zweimal instanziiert werden. – NichtJens

1

Was Sie tun, ist so idiomatisch wie es sein kann. Einige Sprachen haben einen binären Präfix-Operator namens do, die ihr Argument aufruft:

generator = do <generator expression> 

Python Syntax funktioniert einfach nicht zulassen, dass Sie mehrzeilige Funktionen als Ausdrücke schreiben. Sie müssen def Anweisungen verwenden. Deshalb sollten Sie nicht eine mehrzeilige Funktion oder Generator Dekorateur mit einem normalen Aufruf in Python entweder passieren können:

generator = decorator(def generator(): yield) # syntax error 

Sie haben die Funktion zu definieren oder Front Generator:

def generator(): yield 
generator = decorator(generator) 

Es ist vor allem Diese syntaktische Einschränkung, die Python gezwungen hat, die @-Syntax für Dekorationsfunktionen zu übernehmen, die mit einer def-Anweisung definiert sind. Der folgende Code entspricht dem letzten Beispiel (abgesehen von einem feinen Unterschied in wenn die Namen zugeordnet sind):

@decorator 
def generator(): yield 

Als Greg erwähnt, gibt Generator Comprehensions sind, die wirklich gut funktionieren, wo sie nutzen zu können , aber wenn du mehr Logik im Körper brauchst, dann scheint das, was du machst, der pythischste Weg zu sein. Es ist nur so, dass die Python-Syntax die funktionale Programmierung immer ziemlich klobig gemacht hat.


Sie gebeten, in den Kommentaren zu den Sprachen, die die do Betreiber haben. CoffeeScript und Haskell haben es beide.Um ehrlich zu sein, verstehe ich nicht wirklich anything the Haskell docs say, aber in CoffeeScript, do ist nützlich für das Erstellen von Werten innerhalb von Schließungen.

Werfen Sie einen Blick auf diesen Code (es folgt eine Erklärung).

updateElementText = do -> 
    element = jQuery "#someElement" 
    return (newText) -> element.text newText 

, die wie folgt in JS aussehen:

let updateElementText = function() { 
    let element = jQuery("#someElement"); 
    return function(newText) { element.text(newText) }; 
}(); // Immediately-Invoked Function Expression (IIFE) 

Der Code updateElementText zur Funktion zuweist, die auf der Linie 3, die von der Schließung element verweist zurückgegeben. Das Element wird nur einmal (in Zeile 2) von jQuery ausgewählt, unabhängig davon, wie oft updateElementText aufgerufen wird, um das Element zu aktualisieren. Beachten Sie auch, dass element ausgeblendet ist, sodass nur von der Funktion, die es aktualisiert, referenziert werden kann.

+0

Haben Sie Gedanken darüber, welche Sprachen einen solchen "do" -Operator haben? Nachdem ich die Antworten gelesen hatte, konnte ich feststellen, dass es gut war, wenn ich viele dieser Einzelinstanzen-Generatoren hatte. Aber ich nicht - es ist nur einer. Also, ich denke, Kindalls Antwort ist genau richtig. – NichtJens

+0

Ich habe ein paar Sachen zu meiner Antwort hinzugefügt, aber ja, Kindall hat Recht. Es ist am besten, es einfach klar zu schreiben und den Namen wiederzuverwenden, wenn du das nur einmal tust. Wenn Sie das gleiche Muster an mehreren Stellen hätten, wäre Ihr Originaldekorateur besser. –

+0

Ah. Jetzt sehe ich, wie das mit meiner Frage verglichen wird. Danke, dass du diese Verbindung hergestellt hast. Zugegebenermaßen ist das Haskell-Beispiel auch mir nicht gewachsen ;-) Ich denke, man kann nicht einfach zum fortgeschrittensten Thema einer unbekannten Sprache springen und erwarten, es zu verstehen. Scheint so, Zeit, um Haskell zu lernen. – NichtJens

Verwandte Themen