Seite 1 von 2
[C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 02:12
von theliquidwave
Hallo.
Wie realisiere ich es, dass ich auch Klassenfunktionen (Memberfunktionen, blabla) aufrufen kann? Also sowas:
Code: Alles auswählen
class X(object):
def __init__(self):
cmodule.register(self.func)
def func(self, blah):
print blah
def func_noclass(blah):
print blah
cmodule.register(func_noclass)
Der Aufruf zu func_noclass funktioniert problem, X.func crasht aber.
Hier mal der Codeausschnitt:
http://www.python-forum.de/pastebin.php?mode=view&s=12
Gibt es eigentlich sowas ähnliches wie PyEval_Check oder so? Ich konnte nichts dergleichen finden.
Edit: Gibt es eigentlich eine Möglichkeit, ein C-Modul wieder zu entladen?
Gruß
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 13:33
von Dauerbaustelle
Ich glaube, das ist ein Denkfehler deinerseits. `X.func` operiert doch immer AUF einem Objekt von `X` (welches per Konvention an den Namen `self` gebunden wird) -- wenn du jetzt also `X.func(42)` aufrufst, gibt es ja gar kein Objekt, auf dem die Methode operieren kann!
Vielleicht suchst du aber auch nur einfach sowas wie
@staticmethod oder
@classmethod.
Edit: `PyEval_Check`? Was soll das tun?
Edit2: "Entladen": Ich glaube, das geht, indem du alle Referenzen auf das Modulobjekt entfernst (also schlussendlich auch aus `sys.modules` entfernst). Aber warum willst du das?
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 13:41
von Trundle
Ich weiß nicht, wieviele sinnvolle Antworten du dir daurauf erhoffst, aber zu "X.func crasht" kann man ohne den Quelltext zu sehen einfach gar nichts sagen (ohne zu raten). Bei C-Extensions ist beispielsweise die Referenzzählerei sehr wichtig, und davon sieht man exakt gar nichts.
Und der Fetzen Code, den du da zeigst, ist auch noch falsch: Der Rückgabewert von `Py_BuildValue` wird nicht überprüft und außerdem müsstest du explizit ein Tupel erstellen, wenn du das `PyEval_CallObject` übergibst (also "(s)" anstatt "s").
Chrisber hat geschrieben:Edit: Gibt es eigentlich eine Möglichkeit, ein C-Modul wieder zu entladen?
Nein.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 14:36
von Dauerbaustelle
Am schönsten, weil expliziter, finde ich da
Code: Alles auswählen
PyObject* args = PyTuple_Pack(/* size */ 1, ...);
if(args == NULL)
stirb();
PyObject* rv = PyObject_CallObject(..., args);
/* Nicht vergessen: */
Py_DECREF(args);
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 14:40
von theliquidwave
Hi.
@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
PyEval_Check - damit meine ich ein Äquivalent zu ``callable``. Ich habe jetzt das hier gefunden:
http://docs.python.org/c-api/object.htm ... able_Check
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?
@ Trundle: Ich bekomme die Callbackfunktion über PyArg_ParseTuple - muss ich da einen Reference Count ausführen?
Das mit dem Py_BuildValue wusste ich nicht, danke!
Gruß
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 15:17
von Darii
Chrisber hat geschrieben:@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
Dafür ist static-/classmethod nunmal da. Wenn du das nicht verwenden willst musst du die Instanz/Klasse halt selbst übergeben.
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?
Nein, das heißt, dass die Funktion immer funktioniert. Also du immer eine 0 oder 1 zurück kriegst und nichts anderes.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 15:19
von Dauerbaustelle
Chrisber hat geschrieben:Hi.
@ Dauerbaustelle: Das ich nicht X.func registriere ist mir auch klar. Ich wollte damit nur zeigen, dass ich die Funktion von der Klasse X meine.
Das muss auch ohne staticmethod gehen...
Dann zeig doch mal, was genau der Fehler ist, den du bekommst.
Problem ist jedoch, dass mich der Satz dort verwirrt: "This function always succeeds." - heißt das, dass es immer 1 returnt?
Return 1 if the object is callable and 0 otherwise
Ja, genau, es kommt immer 1 zurück.
Ich bekomme die Callbackfunktion über PyArg_ParseTuple - muss ich da einen Reference Count ausführen?
Du musst den Reference-Count erhöhen, wenn du `PyArg_ParseTuple` verwendest, ja. Trundle meinte aber was anderes.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 15:33
von theliquidwave
Hi.
Danke erstmal.
@ Dauerbaustelle: Fehler ist der sofortige Crash des Programms... Was soll ich dir da denn liefern?
Wenn ich den Reference Count erhöht habe, dann nach dem beenden der Funktion wieder einen "De"-Reference Count ausführen, richtig?
Gruß
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 16:13
von Trundle
Ein kleiner auf das Minimum reduzierter Testfall, der das Problem aufzeigt, wäre ganz, denn dann sieht man was du machst und muss nicht wild im Dunkeln rumstochern.
Prinzipiell erniedrigst du den Referenzzähler eben wieder, wenn du den Wert "wegschmeißt" - also beispielsweise, wenn deine `register`-Funktion einen neuen Callback speichert.
Oder du benutzt einfach Cython und machst dir um all das überhaupt keine Gedanken mehr.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 16:47
von BlackJack
@Trundle: Refrenzzähler sind hart im Nehmen, die kann man nicht so leicht erniedrigen. Verringern kann man sie aber.

Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 16. Mai 2010, 19:25
von Dauerbaustelle
Chrisber, gdb-Traceback zum Beispiel...
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 15:01
von theliquidwave
Hi.
Ich habe es nun hingekriegt. Problem war, dass ich (durch einige Umstände im Code) die Klasse immer vorm Aufrufen gelöscht habe -.- (ja, ich schäme mich!)
Dennoch habe ich jetzt ein weiteres Problem: Wenn ich einen Callback einer statischen Funktion speichere (also den PyObject-Pointer), funktioniert das wunderbar (ich kann ihn später problemlos wiederfinden). Jedoch funktioniert das ganze nicht, wenn ich einen Callback auf eine Memberfunktion speichern will. Beispiel:
Code: Alles auswählen
myplugin.bla.hook("xblub", xblub) # Adresse von xblub z.B. 0x10a7bbf0
myplugin.bla.hook("instanceblub", instance.blub) # Adresse von instance.blub HIER z.B. 0x1029b1e8
# direkt danach
myplugin.bla.unhook("xblub", xblub) # Adresse von xblub ist immernoch 0x10a7bbf0 - kann ich als problemlos wiederfinden
myplugin.bla.unhook("instanceblub", instance.blub) # Adresse von instance.blub hat sich geändert: 0x1029b210
Problem ist nun natürlich, dass ich das nicht mehr wiederfinden kann, dementsprechend den Hook nicht entfernen kann - und das resultiert später in einem Fehler.
Hat da jemand eine Idee, wie ich das Problem lösen könnte? Gibt es da vielleicht so etwas wie PyInstance_IsInstance(<adresse vom hook>, <adresse vom unhook>) oder so?
Edit: In der API steht bei PyList_Append leider nichts davon, ob es einen neuen Reference Count erstellt oder nicht. Logisch wäre es natürlich. Was meint ihr dazu?
Gruß
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 16:00
von Dauerbaustelle
Eigentlich darf sich die Adresse nicht ändern. Warum sollte sie? Ich denke, das Problem sitzt wo anders :-)
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 16:08
von theliquidwave
Naja. Was sollte hier denn falsch sein?
Code: Alles auswählen
import es, nativetools as nt
def load():
nt.concommand.hook("maps", maps_hook)
nt.concommand.hook("maps", maps_hook2)
nt.concommand.hook("maps", instance.maps_hook3) // hier die Klasseninstanz
nt.concommand.hook("status", status_hook)
nt.server.registerMapEndCallback(map_end)
def unload():
nt.concommand.unhook("maps", maps_hook)
nt.concommand.unhook("maps", maps_hook2)
nt.concommand.unhook("maps", instance.maps_hook3) // hier wird es dann nicht mehr gefunden da die Adresse anders ist
nt.concommand.unhook("status", status_hook)
nt.server.unregisterMapEndCallback(map_end)
def maps_hook(args):
es.msg("Hook 1")
return False
def maps_hook2(args):
es.msg("Hook 2")
return False
class mapsHook(object):
def maps_hook3(self, args):
es.msg("Hook 3")
es.fail()
return False
instance = mapsHook()
def status_hook(args):
es.msg("Status Command executed!")
es.msg("UserID: %i" % nt.concommand.getUserID())
return False
def map_end():
es.msg("Map ends now!")
Das ist das ganze Script (ungekürzt um Fehler nicht wegzuschneiden). Mein Plugin ist nativetools.
Gruß
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 16:24
von Dauerbaustelle
Ich meine im C-Programm.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 16:52
von BlackJack
@Chrisber: Du musst die Objekte vergleichen. `m` ist hier eine Methode:
Code: Alles auswählen
In [811]: a = A()
In [812]: a.m is a.m
Out[812]: False
In [813]: a.m == a.m
Out[813]: True
@Dauerbaustelle: Wenn man von einem Exemplar eine Methode abfragt wird da jedesmal ein *neues* Objekt zurückgegeben und das liegt halt nicht immer an der selben Adresse.
Re: [C] Klassenfunktion aufrufen
Verfasst: Sonntag 23. Mai 2010, 23:01
von Dauerbaustelle
BlackJack, danke, hatte ich noch nicht gewusst -- aber die `id(meth)` sind gleich? Dann hab ich wohl `id` falsch verstanden...
Re: [C] Klassenfunktion aufrufen
Verfasst: Montag 24. Mai 2010, 00:22
von BlackJack
@Dauerbaustelle: Nein die `id()`-Werte sind nicht gleich. Wären sie gleich würde ``is`` ja `True` ergeben.
Re: [C] Klassenfunktion aufrufen
Verfasst: Montag 24. Mai 2010, 01:01
von Dauerbaustelle
BlackJack hat geschrieben:@Dauerbaustelle: Nein die `id()`-Werte sind nicht gleich. Wären sie gleich würde ``is`` ja `True` ergeben.
Seltsam :-/
Code: Alles auswählen
In [1]: class Foo(object):
...: def meth(self):
...: pass
...:
In [2]: foo = Foo()
In [3]: foo.meth is foo.meth
Out[3]: False
In [4]: id(foo.meth) == id(foo.meth)
Out[4]: True
Re: [C] Klassenfunktion aufrufen
Verfasst: Montag 24. Mai 2010, 05:04
von BlackJack
@Dauerbaustelle: In beiden Fällen hat man einen binären Operator bei dem erst das linke und dann das rechte Argument ausgewertet wird.
Fall 1: Links: ``foo.meth`` erzeugt ein aufrufbares Objekt an einer bestimmten Adresse. Rechts: ``foo.meth`` erzeugt ein anderes aufrufbares Objekt an einer anderen Adresse. ``is`` vergleicht die Identität der beiden unterschiedlichen Objekte.
Fall 2: Links: ``id(foo.meth)`` erzeugt ein aufrufbares Objekt an einer bestimmten Adresse, übergibt es an die `id()`-Funktion, die liefert eine Zahl, das aufrufbare Objekt wird nicht mehr gebraucht und der Speicher wird wieder freigegeben. Rechts: ``id(foo.meth)`` erzeugt ein aufrufbares Objekt an der eben gerade freigewordenen Adresse an der auch das andere Objekt lag. Die `id()`-Funktion liefert die gleiche Zahl. ``==`` ergibt bei gleichen Zahlen konsequenterweise `True`.
`id()` garantiert nur unterschiedliche Werte bei Objekten die gleichzeitig existieren. Andererseits darf man sich auf das beobachtete Verhalten auch nicht verlassen, denn das CPython für die `id()`-Funktion die Adresse liefert ist ein Implementierungsdetail. Die Implementierung könnte genausogut jedem erzeugten Objekt eine "Seriennummer" verpassen die hochgezählt wird. Jython macht das zum Beispiel so wenn ich mich recht erinnere.