2008-10-03 9 views
23

Wie schreibe ich einen Dekorator, der das aktuelle Arbeitsverzeichnis auf das wiederherstellt, was es war, bevor die dekorierte Funktion aufgerufen wurde? Mit anderen Worten, wenn ich den Decorator für eine Funktion verwende, die ein os.chdir() ausführt, wird das cwd nicht geändert, nachdem die Funktion aufgerufen wurde.Python - Wie schreibe ich einen Dekorateur, der die cwd wiederherstellt?

+1

Und du hast die Frage gestellt und selbst in 3 Minuten beantwortet, weil ...? Offensichtlich hatten Sie die Antwort (die kaum verbessert werden kann), noch bevor Sie die Frage stellten. Ich würde wirklich gerne deine Argumentation wissen. – tzot

+6

FAQ sagt "Es ist auch völlig in Ordnung, Ihre eigene Programmierfrage zu stellen und zu beantworten". Es enthält drei erforderliche Kriterien für Fragen, und "Sie kennen die Antwort nicht" gehört nicht dazu. –

+2

Ich hatte den Code geschrieben und dann stellte sich heraus (nach Refactoring), dass ich ihn nicht brauchte. Ich dachte, Stackoverflow ist ein guter Ort, um es zu archivieren, und vielleicht können andere davon profitieren. –

Antwort

13

Das path.py Modul verfügt über einen Kontext-Manager (die Sie wirklich mit Pfaden in Python-Skripte, wenn Umgang verwenden sollten):

subdir = d/'subdir' #subdir is a path object, in the path.py module 
with subdir: 
    # here current dir is subdir 

#not anymore 

(Dank geht an this blog post von Rob erto Alsina)

+1

Wenn path.py jetzt eingebaut ist, sollten Sie vielleicht http://stackoverflow.com/questions/3899761/will-the-real-path-py-please-stand-up beantworten. –

+0

leider ist es nicht, aber danke ich war mir der Frage nicht bewusst – CharlesB

+0

Ich denke, ich habe Ihre Antwort falsch interpretiert. Meintest du, dass der context manager in path.py integriert ist? (Ich dachte du meinst path.py ist jetzt in Python integriert.) –

3
def preserve_cwd(function): 
    def decorator(*args, **kwargs): 
     cwd = os.getcwd() 
     result = function(*args, **kwargs) 
     os.chdir(cwd) 
     return result 
    return decorator 

Hier ist, wie es verwendet wird:

@preserve_cwd 
def test(): 
    print 'was:',os.getcwd() 
    os.chdir('/') 
    print 'now:',os.getcwd() 

>>> print os.getcwd() 
/Users/dspitzer 
>>> test() 
was: /Users/dspitzer 
now:/
>>> print os.getcwd() 
/Users/dspitzer 
+2

Benötigt Fehlerbehandlung, siehe meine Antwort. – codeape

30

Die Antwort für einen Dekorateur gegeben wurde; Es funktioniert bei der Definition der Funktion wie gewünscht.

Mit Python 2.5+, Sie haben auch eine Option, dass Anruf Bühne an der Funktion zu tun, um einen Kontext-Manager:

from __future__ import with_statement # needed for 2.5 ≤ Python < 2.6 
import contextlib, os 

@contextlib.contextmanager 
def remember_cwd(): 
    curdir= os.getcwd() 
    try: yield 
    finally: os.chdir(curdir) 

, die verwendet werden können, wenn bei der Funktionsaufruf Zeit benötigt wie:

print "getcwd before:", os.getcwd() 
with remember_cwd(): 
    walk_around_the_filesystem() 
print "getcwd after:", os.getcwd() 

Es ist eine schöne Option zu haben.

EDIT: Ich fügte Fehlerbehandlung wie von Codeape vorgeschlagen hinzu. Da meine Antwort gewählt wurde, ist es fair, eine vollständige Antwort anzubieten, alle anderen Fragen beiseite.

+0

* Und * es kann verwendet werden, um den oben genannten Dekorator zu schreiben :) – Constantin

+1

Benötigt Fehlerbehandlung, siehe meine Antwort. – codeape

+0

Benötigt die Fehlerbehandlung einen expliziten try/finally? Ich dachte, der Punkt der Kontextmanager war, dass MANAGER .__ exit__ immer aufgerufen wurde. Aber ich habe den Decorator nie aus Contextlib probiert, also weiß ich nicht, was die Probleme sind. –

17

Die angegebenen Antworten berücksichtigen nicht, dass die umbrochene Funktion eine Ausnahme auslösen kann. In diesem Fall wird das Verzeichnis niemals wiederhergestellt. Der folgende Code fügt die Ausnahmenbehandlung zu den vorherigen Antworten hinzu.

als Dekorateur:

def preserve_cwd(function): 
    @functools.wraps(function) 
    def decorator(*args, **kwargs): 
     cwd = os.getcwd() 
     try: 
      return function(*args, **kwargs) 
     finally: 
      os.chdir(cwd) 
    return decorator 

und als Kontext-Manager:

@contextlib.contextmanager 
def remember_cwd(): 
    curdir = os.getcwd() 
    try: 
     yield 
    finally: 
     os.chdir(curdir)