[Python-de] subprocess mit callback

Florian Lindner mailinglists at xgm.de
Fr Jul 8 16:35:23 CEST 2011


Am Dienstag 05 Juli 2011, 00:35:06 schrieb Peter Otten:
> Florian Lindner wrote:
> > Am Montag 04 Juli 2011, 13:57:09 schrieb Peter Otten:
> >> Florian Lindner wrote:
> >> > Am Samstag 02 Juli 2011, 17:35:00 schrieb Stefan Schwarzer:
> >> >> Hallo Florian,
> >> >> 
> >> >> On 2011-07-02 12:25, Florian Lindner wrote:
> >> >> > Am Dienstag 28 Juni 2011, 21:10:46 schrieb Hans-Peter Jansen:
> >> >> >> On Tuesday 28 June 2011, 16:25:04 Florian Lindner wrote:
> >> >> > async = Async(threading.Thread)
> >> >> > 
> >> >> > In [7]: @async
> >> >> > 
> >> >> >    ...: def add(b):
> >> >> >    ...:     return b+2
> >> >> > 
> >> >> >    ...:
> >> >> > In [8]: add(4)
> >> >> > Out[8]: <Thread(add-1, stopped 139819357185792)>
> >> >> > 
> >> >> > Das klappt soweit alles ganz gut. Wenn ich allerdings die callback
> >> >> > funktionen benutzen will, bekomme ich es nicht hin:
> >> >> > 
> >> >> > In [9]: def os(result):
> >> >> >    ...:     print "Success", result
> >> >> 
> >> >> ich würde eher davon abraten, eine Funktion so zu nennen wie
> >> >> ein häufig benutztes Modul aus der Standard-Bibliothek. Das
> >> >> kann leicht zu Verwirrung führen.
> >> > 
> >> > Klaro, das war so nur in die Shell gehackt.
> >> > 
> >> >> > TypeError: __call__() takes at least 2 arguments (2 given)
> >> >> 
> >> >> Die Methode akzeptiert mindestens zwei Parameter, die du
> >> >> auch angegeben hast, aber du bekommst einen TypeError dazu?
> >> >> Hast du die Fehlermeldung richtig übernommen?
> >> > 
> >> > Ein komplettes Skript schaut jetzt so aus:
> >> > 
> >> > from common import Async
> >> > import threading
> >> > 
> >> > async = Async(threading.Thread)
> >> > 
> >> > def o_s(result):
> >> >     print "Success: ", result
> >> > 
> >> > @async(on_success=o_s)
> >> > 
> >> > def add(x):
> >> >     return x+2
> >> > 
> >> > res = add(2)
> >> > print res
> >> > 
> >> > und wenn es ausgeführt wird:
> >> > 
> >> > florian at horus ~/SA/src (git)-[master] % python2 test.py
> >> > 
> >> > Traceback (most recent call last):
> >> >   File "test.py", line 9, in <module>
> >> >   
> >> >     @async(on_success=o_s)
> >> > 
> >> > TypeError: __call__() takes at least 2 arguments (2 given)
> >> > 
> >> > Version von python ist 2.7.2.
> >> 
> >> Faszinierende Fehlermeldung; Python 2.6 ist dagegen richtig langweilig:
> >> > $ python async.py
> >> 
> >> Traceback (most recent call last):
> >>   File "async.py", line 68, in <module>
> >>   
> >>     @async(on_success=on_success)
> >> 
> >> TypeError: __call__() takes at least 2 non-keyword arguments (1 given)
> >> 
> >> > Wenn ich den Dekorator ohne Argumente
> >> > benutze funktioniert es, aber halt kein Callback.
> >> 
> >> Probier es mal zu Fuß:
> >> 
> >> def add(x):
> >>     return x + 2
> >> 
> >> add = async(add, on_success=o_s)
> > 
> > Das funktioniert:
> > 
> > florian at horus ~/SA/src (git)-[master] % python2 test.py
> > Success: <Thread(add-1, started 140544079726336)>
> > 
> >  4
> > 
> > bei Python 2.6 ebenfalls? Hast Du eine Idee woran es liegen könnte?
> 
> Ja klar.
> 
> @async(on_success=callback)
> def add(x):
>    return x + 2
> 
> entspricht
> 
> def add(x):
>    return x + 2
> add = async(on_success=callback)(add)
> 
> Dafür ist Async.__call__() nicht ausgelegt; es erwartet immer die zu
> dekorierende Funktion als (abgesehen von self) erstes Argument. Hier eine
> modifizierte Version, bei der die __call__-Methode eine Fallunterscheidung
> vornimmt:
> 

[...]
> 
> Ich habe versucht, so wenig wie möglich an der Klasse zu ändern.

Super! Vielen Dank, es klappt so!

Allerdings hören mein Problemchen mit dem Decorator da nicht auf. ;-) Als ich 
ihn in meinen eigentlichen Code eingebaut habe:

    @async(on_sucess = self._cb_run_success)
    def run(self):
        pass

das Problem ist nun, dass self für den Decorator gar nicht verfügbar ist.

Mir fallen da erstmal zwei "Lösungen" ein (in Anführungszeichen, da beide 
unerprobt)

1) den Callback _cb_run_success zur classmethod machen, damit braucht er kein 
self mehr. Allerdings greife ich im in der Methode schon auf self zu, müsste 
meinen Code also irgendwie umbauen. Damit könnte ich die on_success Methode 
auch gleich komplett außerhalb der Klase stellen.

2) In __init__ der Klasse den Dekorator partial instanziieren (<- richtiger 
Begriff?) 

def __init__(self):
	self.my_async = functools.partial(async(on_success = self.o_s))

Leider - wie ich eben gerade merke - ist damit das Problem nur verschoben. 
@self.my_async funktioniert leider ebensowenig.

Ich habe dazu eine Diskussion gefunden 
http://stackoverflow.com/questions/3631768/is-it-bad-practice-to-use-self-in-
decorators

Man kann wohl self im Code des Decorators bekommen, indem man Element [0] der 
positional Arguments ausliest *args. Das würde die allgemeine Verwendbarkeit 
des Dekorators wohl leider ziemlich einschränken.

Grüße und vielen Dank für alle weiteren Tipps!

Florian


Mehr Informationen über die Mailingliste python-de