2016-06-20 9 views
0
ausblenden

Ich habe eine Funktion f, die zwei Parameter zurückgibt. Nach dieser Funktion verwende ich den ersten Parameter. Ich brauche den zweiten, um Dinge mit einer anderen Funktion zusammen zu kleben g. So würde mein Code wie folgt aussehen:Post-Funktionsaufrufe mit der Anweisung

a, b = f() 
# do stuff with a 
g(b) 

Diese sehr repetitiv ist, so dass ich dachte ich so etwas wie ein RAII Ansatz nutzen könnten. Aber, da ich weiß nicht, wann Objekte werden sterben und mein Hauptziel ist der repetitiven Code, um loszuwerden, habe ich die with Aussage:

with F() as a: 
    # do stuff with a 

Das ist es. Ich habe im Grunde um diese Funktion ein Objekt F erstellt, das __enter__ und __exit__ Funktionen liefert (und natürlich __init__ Funktion).

Allerdings frage ich mich immer noch, ob dies die richtige "Pythonic" -Methode ist, dies zu handhaben, da eine with-Anweisung Ausnahmen behandeln sollte. Vor allem diese __exit__ Funktion hat drei Argumente, die ich im Moment nicht verwende.

Edit (weitere Erklärungen): Immer wenn ich f() anrufen muss ich etwas mit b tun. Es ist egal, was dazwischen passiert ist. Ich drückte es als g(b) aus. Und genau das verberge ich in der with-Anweisung. So muss der Programmierer nach jedem Aufruf von f() nicht mehr g(b) eingeben, was sehr unordentlich und verwirrend werden könnte, da #do stuff with a langwierig werden könnte.

+0

* Ist * es sehr eintönig? Es ist nicht klar, was das Problem ist, oder warum Sie einen Kontextmanager für die beste Lösung hielten. Wenn Sie diesen Code häufig verwenden, stellen Sie eine Funktion mit den Parametern "a" und "b" her. Wenn sich das Bit in der Mitte ändert, also glauben Sie, dass Sie es nicht extrahieren können, fügen Sie einen neuen Parameter hinzu, der mit 'a' vor Aufruf von' g' aufgerufen wird (beachten Sie, dass Funktionen in Python die erste Klasse sind). Können Sie ein weniger abstraktes Beispiel dafür geben, was Sie erreichen möchten? Was macht 'g' *? – jonrsharpe

+1

Können Sie erklären, wie genau es sich wiederholt? Außerdem, wie verwenden Sie derzeit "b" in Ihrer with-Anweisung? Persönlich bevorzuge ich 'mit' für Dinge, die wie Ressourcen aussehen. Die andere Alternative ist die Verwendung von Dekoratoren. – SuperSaiyan

+0

Was genau ist 'g'? Sollte es dennoch passieren, wenn eine Ausnahme ausgelöst wird? Wenn es nicht passieren sollte, sollte stattdessen eine andere Art von Säuberung passieren? – user2357112

Antwort

1

Eine etwas pythonic Art und Weise tun, die contextlib Modul zu verwenden wäre. Sie können Ihre eigene Klasse mit den Funktionen __enter__ und __exit__ ausrollen.

from contextlib import contextmanager 

def g(x): 
    print "printing g(..):", x 

def f(): 
    return "CORE", "extra" 

@contextmanager 
def wrap(): 
    a, b = f() 
    try: 
     yield a 
    except Exception as e: 
     print "Exception occurred", e 
    finally: 
     g(b) 
if __name__ == '__main__': 
    with wrap() as x: 
     print "printing f(..):", x 
     raise Exception() 

Ausgang:

$ python temp.py 
printing f(..): CORE 
Exception occurred 
printing g(..): extra 
+0

Ich glaube, du hast den '@ contextmanager' Dekorator auf die falsche Funktion gesetzt. – user2357112

+0

Ja, es war ein Copy-Paste-Problem. Behoben, danke! – SuperSaiyan

+0

Ja, das wollte ich. Aber wenn ich (in Zukunft) mit Ausnahmen umgehen muss, brauche ich meine eigene Klasse mit Exit- und Entry-Funktion, oder? – hr0m

1

Für jemand anderen, der Ihren Code liest (oder Sie in 6 Monaten), wird ein Kontextmanager wahrscheinlich die Erstellung/Initialisierung und Schließung/Löschung einer Ressource implizieren, und ich würde sehr vorsichtig sein, das Idiom neu zu verwenden.

Sie könnten Zusammensetzung verwenden, anstatt ein Kontext-Manager:

def func(a, b): 
    # do stuff with a & optionally b 

def new_f(do_stuff): 
    a, b = f() 
    do_stuff(a, b) 
    g(b) 


new_f(func) 
+0

Das ist nett, aber leider keine Option, da muss ich sei viel allgemeiner. Sehen Sie es als komplizierten 'do_stuff', der nicht mit nur einer Funktion bearbeitet werden kann – hr0m

+0

Sie können sicher mehrere Anrufe in eine einzige Funktion ziehen. Gut benannte und gut dokumentierte Funktionen sind besser als große Code-Blöcke. –