2013-05-15 3 views
59

Ich verwende eine Python-Bibliothek, dieWie wird die Ausgabe von stdout von einem Python-Funktionsaufruf erfasst?

do_something(my_object) 

und es ändert sich etwas an ein Objekt tut. Dabei druckt es einige Statistiken aus, und ich möchte diese Informationen im Griff haben. Die richtige Lösung wäre do_something() zu ändern, um die relevanten Informationen zurückzukehren,

out = do_something(my_object) 

aber es wird eine Weile dauern, bis die Entwickler von do_something() zu diesem Thema erhalten. Als Workaround dachte ich über Parsing was immer do_something() auf stdout schreibt.

Wie kann ich stdout Ausgabe zwischen zwei Punkten im Code erfassen, beispielsweise

start_capturing() 
do_something(my_object) 
out = end_capturing() 

?

+2

möglich duplicate von [capturing dis.dis Ergebnisse] (http://stackoverflow.com/questions/12111717/capturing-dis-dis-results) –

+0

Meine Antwort in der verknüpften Frage gilt auch hier. –

+0

Ich habe versucht, das einmal zu tun und die beste Antwort fand ich war: http://stackoverflow.com/a/3113913/1330293 – elyase

Antwort

115

Versuchen Sie, diese Kontext-Manager:

from cStringIO import StringIO 
import sys 

class Capturing(list): 
    def __enter__(self): 
     self._stdout = sys.stdout 
     sys.stdout = self._stringio = StringIO() 
     return self 
    def __exit__(self, *args): 
     self.extend(self._stringio.getvalue().splitlines()) 
     del self._stringio # free up some memory 
     sys.stdout = self._stdout 

Verbrauch:

with Capturing() as output: 
    do_something(my_object) 

output ist jetzt eine Liste der Funktionsaufruf die Linien gedruckt enthält.

Erweiterte Nutzung:

Was kann nicht offensichtlich ist, dass dies mehr als einmal getan werden kann und die Ergebnisse verketteten:

with Capturing() as output: 
    print 'hello world' 

print 'displays on screen' 

with Capturing(output) as output: # note the constructor argument 
    print 'hello world2' 

print 'done' 
print 'output:', output 

Ausgang:

displays on screen      
done         
output: ['hello world', 'hello world2'] 
+0

+1 Das ist Kewl, kindal. – martineau

+0

Danke! Und danke, dass du den erweiterten Abschnitt hinzugefügt hast ... Ich habe ursprünglich eine Slice-Zuweisung verwendet, um den aufgenommenen Text in die Liste zu stecken, dann habe ich mich selbst in den Kopf gesteckt und '.extend()' verwendet, damit er konkatenativ verwendet werden kann dir ist aufgefallen. :-) – kindall

+0

Gern geschehen - froh, dass Sie meine Bearbeitung nicht störte. – martineau

27

In Python> = 3.4, contextlib enthält einen redirect_stdout Dekorateur.

import io 
from contextlib import redirect_stdout 

f = io.StringIO() 
with redirect_stdout(f): 
    do_something(my_object) 
out = f.getvalue() 

Von the docs:

Context Manager zum vorübergehenden Umleitung sys.stdout auf eine andere Datei oder Datei-ähnliches Objekt Es kann Ihre Frage zu beantworten, wie so verwendet werden.

Dieses Tool fügt vorhandenen Funktionen oder Klassen, deren Ausgang fest mit stdout verbunden ist, Flexibilität hinzu.

Zum Beispiel wird die Ausgabe von help() normalerweise an sys.stdout gesendet. Sie können diese Ausgabe in einer Zeichenfolge erfassen, indem Sie die Ausgabe an eine io umleiten.StringIO Objekt:

f = io.StringIO() 
    with redirect_stdout(f): 
     help(pow) 
    s = f.getvalue() 

Um die Ausgabe von Hilfe() in eine Datei auf der Festplatte, leiten Sie die Ausgabe an eine normale Datei zu senden:

with open('help.txt', 'w') as f: 
    with redirect_stdout(f): 
     help(pow) 

Um die Ausgabe der Hilfe(), um sys zu senden .stderr:

with redirect_stdout(sys.stderr): 
    help(pow) 

Beachten Sie, dass die globale Nebenwirkung auf sys.stdout bedeutet, dass dieser Zusammenhang Manager für die Verwendung in der Bibliothek Code nicht geeignet ist und die meisten Gewinde Anwendungen. Es hat auch keine Auswirkung auf die Ausgabe von Teilprozessen. Es ist jedoch immer noch ein nützlicher Ansatz für viele Hilfsskripts.

Dieser Kontextmanager ist reentrant.

Verwandte Themen