2014-06-19 13 views
10

Mam raczej nietypową prośbę, myślę ... wytłumaczę dlaczego po tym, jak wyjaśnię co.Python - wykrywa, kiedy mój obiekt jest zapisany na standardowe wyjście?

Co

Chcę wykryć, kiedy mój obiekt jest zapisywany na standardowe wyjście, tak że mogę wykonywać skutki uboczne w tym czasie. Tak więc, na przykład, kiedy piszę:

sys.stdout.write(instance_of_my_class) 

powinien wykonywać działania niepożądane. Zrobiłem moja klasa jest podklasą str i overrode __call__, __unicode__, __str__, __repr__, index, decode, encode, format, __format__, __getattribute__, __getitem__, a __len__ tak, że każdy z nich drukuje oświadczenie wskazujące, że już został wywołany, ale wygląda na to, że sys.stdout.write nie wywołuje żadnego z nich w celu wydrukowania obiektu.

Zauważ, że jestem specjalnie mówić o sys.stdout.write a nie, na przykład, print - Odkryłem, że print rozmowy __str__ na cokolwiek to jest podane.

Dlaczego

To pytanie nadal z którym odpowiedź na Colored Python Prompt in Windows? zostało przerwane.

Znalazłem, że za każdym razem, gdy pyton musi wyświetlać interaktywny monit, wywołuje __str__ na sys.ps1 i sys.ps2, a następnie zapisuje wyniki, które mają być wyświetlane w linii poleceń. Oznacza to, że wszelkie efekty uboczne w sys.ps2.__str__ są wywoływane zaraz po tych w sys.ps1.__str__, ale chcę je czekać, aż nadejdzie czas wyświetlania sys.ps2.

Więc zamiast zwrócić str w sys.ps2.__str__, byłem na moje podklasę str, która mam nadzieję będzie jakoś być zdolne do połowu kiedy sys.stdout.write nazywa się na nim.

Odpowiedz

1

Dlaczego nie 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

To interesujący pomysł. Zastanawiam się, czy naprawdę zrobiłbym to, co chcę ... Jest wiele naprawdę fajnych rzeczy, które można łatwo modyfikować zachowanie standardowego REPL, jeśli to zadziała. Zajmę się tym później wieczorem i dam ci znać, czy zadziała, czy nie (i zaakceptuję twoją odpowiedź, jeśli to zadziała). – ArtOfWarfare

+0

+1 Ben: Działa to w odpowiedzi na pytanie, które zadałem, ale niestety nie wykrywa, kiedy sys.ps2 jest drukowane. – ArtOfWarfare

+0

Możesz po prostu sprawdzić, czy "jeśli jest sys.ps2" – Ben

4

Intrygujący problem! Domyślam się, że sys.stdout.write nie wywołuje metody __str__, ponieważ Twój obiekt już jest jest a str (lub przynajmniej jego podklasa, która jest wystarczająco dobra dla wszystkich celów i celów) ... więc nie są potrzebne żadne metody rzutowania .

Dalsze badania sugerują, że sys.stdout.write naprawdę nie zawsze chcą wywołać metodę __str__ ...

podejście Podklasa

Przy odrobinie introspekcji, można dowiedzieć się, jakie metody swojej str podklasy nazywany przez sys.stdout.write (odpowiedź brzmi, nie wiele):

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) 

bieganie w Enviro Unicode nment (Python 2.7, ipython notebook), to drukuje:

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

Wydaje się raczej kludge-y, ale można zastąpić podklasy za decode metodę wykonywania pożądanych efektów ubocznych.

Jednak w środowisku innym niż Unicode nie ma wyszukiwania atrybutów.

podejście Wrapper

Zamiast używać podklasę str, może to, czego potrzebujesz to pewnego rodzaju „wrapper” wokół str. Oto brzydki rozpoznawcza siekać który tworzy klasę, która przekazuje większość jego atrybutów do str, ale który nie jest ściśle ich podklasy:

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) 

W środowisku Unicode, co daje w zasadzie te same wyniki:

*** 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> 

jednak, kiedy uruchamiane w środowisku innym niż Unicode, definitely_not_a_string daje komunikat o błędzie:

TypeError: expected a character buffer object 

... to s hows, że metoda .write przechodzi bezpośrednio na poziom C buffer interface, kiedy nie trzeba wykonywać żadnego dekodowania Unicode.

Mój wniosek

Wydaje się, że przesłanianie metody decode jest możliwe kludge w środowisku Unicode, ponieważ sys.stdout.write wywołania tej metody musi dekodować str do Unicode.

Jednak w środowiskach nieobsługujących kodu Unicode wydaje się, że .write nie wykonuje żadnych wyszukiwań atrybutów, ale po prostu przechodzi bezpośrednio do protokołu buforowania znaków na poziomie C, więc nie ma możliwości przechwycenia jego dostępu z kodu Pythona. Rzeczywiście, help(sys.stdout.write) weryfikuje, czy jest to funkcja wbudowana (napisana w C, nie w Pythonie).

+0

Niestety, pracuję w środowisku innym niż Unicode. Wspomniałeś, że nie można przechwycić kodu Pythona, ponieważ jest to kod C. Czy w jakiś sposób można go uchwycić z kodu C? – ArtOfWarfare

+1

Cóż, możesz * napisać niestandardową podklasę 'str' w C, która jakoś podpięte do protokołu bufora, aby powiadomić twój kod Pythona, aby zrobił coś specjalnego. Zaczyna to brzmieć jak anti-pattern: próbujesz "oszukać" 'sys.stdout.write', aby zobaczyć inny widok ciągu promptowego niż cały inny kod. Dlaczego nie wystarczy zmodyfikować lub zamienić kod, który uruchamia 'sys.stdout.write' w pierwszej kolejności? –

+0

Kod uruchamiający 'sys.stdout.write' to interpreter interaktywny Pythona - część, która wypisuje prompt (zwykle' >>> 'dla ps1 i' ... 'dla ps2). Sprawdziłem możliwość modyfikacji, ale nie mogłem znaleźć odpowiedniego pliku w źródłowej dystrybucji Pythona. W tym momencie bardziej interesuje mnie słuchanie, czy istnieje jakieś rozwiązanie, a nie implementacja jednego, tak myślę - to jest zbyt wielkim problemem, aby zmienić '' 'w cmd na zielony zamiast białego - już działa całkowicie na każdym systemie * nix, a ">>>" jest zielone nawet w systemie Windows. – ArtOfWarfare

Powiązane problemy