2014-06-19 10 views
10

Ich habe eine ziemlich ungewöhnliche Anfrage, ich denke ... Ich werde das Warum erklären, nachdem ich das was erklärt habe.Python - erkennen, wenn mein Objekt in stdout geschrieben ist?

Was

Ich möchte erkennen, wenn mein Objekt auf diese geschrieben wird, so dass ich Nebenwirkungen zu dieser Zeit durchführen können. Also, zum Beispiel, wenn ich tippe:

sollte es Nebenwirkungen ausführen. Ich habe meine Klasse eine Unterklasse von str und overrode __call__, __unicode__, __str__, __repr__, index, decode, encode, format, __format__, __getattribute__, __getitem__ und __len__ so gemacht, dass jeder von ihnen eine Erklärung druckt darauf hinweist, dass sie haben wurde aufgerufen, aber es scheint, dass sys.stdout.write keine von denen aufruft, um ein Objekt zu drucken.

Bitte beachte, dass ich speziell rede über sys.stdout.write und nicht zum Beispiel print - Ich habe festgestellt, dass print Anrufe __str__ auf, was es gegeben ist.

Warum

Diese Frage weiter, von wo aus der Antwort auf Colored Python Prompt in Windows? aufhörte.

ich gefunden habe, dass jedes Mal, Python eine interaktive Eingabeaufforderung angezeigt werden muss, ruft es __str__ auf sys.ps1 und sys.ps2, und dann speichert sie die Ergebnisse auf der Kommandozeile angezeigt werden. Dies bedeutet, dass alle Nebenwirkungen in sys.ps2.__str__ direkt nach denen in sys.ps1.__str__ verursacht werden, aber ich möchte diese warten, bis es Zeit ist, sys.ps2 anzuzeigen.

Also anstatt zurückgeben str in sys.ps2.__str__, ich habe meine Unterklasse von str worden Rückkehr, die ich bin der Hoffnung, irgendwie fangen können, wenn sys.stdout.write auf sie aufgerufen wird.

Antwort

1

Warum nicht monkeypatch stdout.write?

stdoutRegistry = set() 

class A(object): 
    def __init__(self): 
     self.stdoutRegistry.add(self) 

    def stdoutNotify(self): 
     pass 

original_stdoutWrite = sys.stdout.write 
def stdoutWrite(*a, **kw): 
    if a in stdoutRegistry: 
     a.stdoutNotify() 
    original_stdoutWrite(*a, **kw) 
sys.stdout.write = stdoutWrite 
+0

Das ist eine interessante Idee. Ich frage mich, ob es wirklich tun würde, was ich will ... es gibt eine Menge wirklich cooler Sachen, um das Verhalten der Standard-REPL leicht zu modifizieren, wenn das funktionieren würde. Ich werde später am Abend darüber nachdenken und Sie wissen lassen, ob es funktioniert oder nicht (und ich werde Ihre Antwort akzeptieren, wenn es funktioniert). – ArtOfWarfare

+0

+1 Ben: Das funktioniert, um die Frage zu beantworten, die ich gestellt habe, aber leider erkennt es nicht wirklich, wenn sys.ps2 gedruckt wird. – ArtOfWarfare

+0

Sie können einfach nach 'if a is sys.ps2' suchen. – Ben

4

Faszinierendes Problem! Meine erste Schätzung ist, dass sys.stdout.write ruft die __str__ Methode nicht, weil Ihr Objekt bereits ist eine str (oder zumindest eine Unterklasse davon, die für alle Absichten und Zwecke gut genug ist) ... so sind keine Casting-Methoden erforderlich .

Weitere Untersuchungen legen nahe, dass sys.stdout.write nicht wirklich jemals die __str__ Methode nennen wollen ...

Subklassen-Ansatz

Mit einer wenig Selbstbeobachtung, können Sie herausfinden, welche Methoden Ihrer str Unterklasse sind genannt durch sys.stdout.write (die Antwort ist, nicht viele):

class superstring(str): 
    def __getattribute__(self, name): 
     print "*** lookup attribute %s of %s" % (name, repr(self)) 
     return str.__getattribute__(self, name) 

foo = superstring("UberL33tPrompt> ") 
sys.stdout.write(foo) 

in einem enviro Unicode Lauf (Python 2.7, ipython Notebook), das druckt:

*** lookup attribute __class__ of 'UberL33tPrompt> ' 
*** lookup attribute decode of 'UberL33tPrompt> ' 
UberL33tPrompt> 

Es scheint eher Flickschusterei-y, aber man konnte die Unterklasse der decode Methode überschreiben, um die gewünschten Nebenwirkungen durchzuführen.

In einer Nicht-Unicode-Umgebung gibt es jedoch keine Attribut-Lookups.

Wrapper Ansatz

eher eine Unterklasse von str als verwenden, müssen Sie vielleicht, was ist eine Art „Wrapper“ um str. Hier ist ein hässlicher explorativer Hack, die eine Klasse erstellt, dass die Delegierten der meisten seiner Attribute zu str, aber das ist nicht unbedingt eine Unterklasse davon

class definitely_not_a_string(object): 
    def __init__(self, s): 
     self.s = s 
    def __str__(self): 
     print "*** Someone wants to see my underlying string object!" 
     return self.s 
    def decode(self, encoding, whatever): 
     print "*** Someone wants to decode me!" 
     return self.s.decode(encoding, whatever) 
    def __getattribute__(self, name): 
     print "*** lookup attribute %s of %s" % (name, repr(self)) 
     if name in ('s', '__init__', '__str__', 'decode', '__class__'): 
      return object.__getattribute__(self, name) 
     else: 
      return str.__getattribute__(self, name) 

foo = definitely_not_a_string("UberL33tPrompt> ") 
sys.stdout.write(foo) 

Im Unicode-Umgebung ergibt dies im Grunde die gleichen Ergebnisse:

*** lookup attribute __class__ of <__main__.definitely_not_a_string object at 0x00000000072D79B0> 
*** lookup attribute decode of <__main__.definitely_not_a_string object at 0x00000000072D79B0> 
*** Someone wants to decode me! 
*** lookup attribute s of <__main__.definitely_not_a_string object at 0x00000000072D79B0> 
UberL33tPrompt> 

Allerdings, wenn ich in einer nicht-Unicode-Umgebung ausführen, definitely_not_a_string eine Fehlermeldung gibt:

TypeError: expected a character buffer object 

... diese s hows, dass die .write Methode direkt auf die C-Ebene buffer interface geht, wenn es keine Unicode-Decodierung durchführen muss.

Mein Fazit

, dass die decode Methode überschrieben scheint ein möglich kludge in Unicode-Umgebungen, da sys.stdout.write ruft diese Methode auf, wenn es eine str in Unicode entschlüsseln muss.

In Nicht-Unicode-Umgebungen scheint es jedoch, dass .write keine Attribut-Lookups ausführt, sondern direkt zum C-level Character Buffer-Protokoll, so dass es keinen Weg gibt, den Zugriff von Python-Code abzufangen. In der Tat, help(sys.stdout.write) überprüft, dass es eine eingebaute Funktion ist (aka geschrieben in C, nicht Python).

+0

Leider arbeite ich in einer Nicht-Unicode-Umgebung. Sie erwähnen, dass es nicht möglich ist, Python-Code abzufangen, da es sich um C-Code handelt. Wäre es möglich, es irgendwie aus C-Code zu erfassen? – ArtOfWarfare

+1

Nun, Sie könnten * eine benutzerdefinierte 'str'-Unterklasse in C schreiben, die irgendwie in das Pufferprotokoll eingebunden ist, um Ihren Python-Code zu benachrichtigen, etwas Spezielles zu tun. Das hört sich jedoch zunächst nach einem Anti-Pattern an: Sie versuchen, "sys.stdout.write" zu "tricksen", um eine andere Ansicht Ihrer Eingabeaufforderungszeichenfolge als alle anderen Codes zu sehen. Warum nicht einfach den Code ändern oder ersetzen, der 'sys.stdout.write' an erster Stelle ausführt? –

+0

Der Code, der 'sys.stdout.write' ausführt, ist der interaktive Python-Interpreter - der Teil, der die Eingabeaufforderung ausgibt (normalerweise '>> für ps1 und' ... 'für ps2). Ich habe die Möglichkeit untersucht, es zu modifizieren, aber ich konnte nicht einmal die relevante Datei in der Python-Quelldistribution finden. An diesem Punkt bin ich mehr daran interessiert zu hören, ob es eine Lösung gibt, als tatsächlich eine zu implementieren, denke ich - das ist viel zu viel Mühe, nur um das "..." in cmd zu ändern, um grün statt weiß zu sein - schon funktioniert komplett auf jedem * nix-System und das '>>>' ist auch unter Windows grün. – ArtOfWarfare