2016-08-28 7 views
1

Wenn ich Callbacks unter Verwendung Py_InitModule registriere, und wenn ich später den Funktionszeiger in der Struktur ändere, um auf eine neue Funktion zu verweisen, wird die neue Funktion aufgerufen. Aber wenn ich den Namen ändere, wird der neue Name nicht erkannt.Py_InitModule kopiert den Namen, aber nicht den Funktionszeiger?

#include <Python.h> 

PyObject* foo1(PyObject *self, PyObject *args) 
{ 
    printf("foo1\n"); 
    Py_RETURN_NONE; 
} 

PyObject* foo2(PyObject *self, PyObject *args) 
{ 
    printf("foo2\n"); 
    Py_RETURN_NONE; 
} 

int main() 
{ 
    PyMethodDef methods[] = { 
     { "foo", foo1, METH_VARARGS, "foo" }, 
     { 0, 0, 0, 0 } 
    }; 

    Py_Initialize(); 
    Py_InitModule("foo", methods); 
    PyRun_SimpleString("import foo\n"); 
    PyRun_SimpleString("foo.foo()\n"); 
    methods[0].ml_meth = foo2; 
    PyRun_SimpleString("foo.foo()\n"); 
    methods[0].ml_name = "foo2"; 
    PyRun_SimpleString("foo.foo()\n"); 
    PyRun_SimpleString("foo.foo2()\n"); 
    return 0; 
} 

Dies ergibt die folgende Ausgabe:

foo1 
foo2 
foo2 
Traceback (most recent call last): 
    File "", line 1, in 
AttributeError: 'module' object has no attribute 'foo2'

Dies scheint ein sehr inkonsistentes Verhalten. Ich fand es zuerst, als ich eine Stapelvariable für PyMethodDef methods verwendete, die das Programm abstürzte, nachdem die Variable den Gültigkeitsbereich verlassen hatte, und ich versuchte immer noch, den C++ - Callback von Python aufzurufen. Also habe ich getestet, dass das Ändern des Zeigers tatsächlich die aufgerufene Funktion ändert, obwohl ich sie nicht erneut mit einem anderen Py_InitModule Aufruf registriert habe. Aber zur gleichen Zeit hat das Ändern des Namens dieses Verhalten nicht.

Bis jetzt bin ich mir ziemlich sicher, dass PyMethodDef so lange leben muss, wie Python-Code versucht, die Methoden aufzurufen (d. H. Nicht Stack/lokale Variable sein), aber nur die Funktionszeiger selbst werden verwendet.

Ist dies ein absichtliches Verhalten oder eine gewisse Aufsicht? Die Dokumentation erwähnt nichts über PyMethodDef Lebenszeit, die ich finden konnte.

+1

Dies wird erwartet. Was denkst du, dass das 'PyInit_Module' nichts initialisiert hat? Es teilt dem Interpreter mit, welches Attribut/welche Methoden definiert sind, um diese später zu vermeiden. Wenn Sie eine Funktion dynamisch umbenennen möchten, müssen Sie 1) das Modulobjekt vom Interpreter beziehen. 2) Verwenden Sie 'PyObject_SetAttr' (oder ähnlich, ich erinnere mich nicht an den genauen Namen), um das Attribut auf dem Modulobjekt zu setzen. – Bakuriu

+0

@Bakuriu Danke, aber warum wird das 'ml_math'-Feld nicht ebenfalls kopiert? Das heißt, warum tauscht das spätere Ändern der Hot-Swap-Funktion die aufgerufene Funktion aus? – sashoalm

Antwort

2

Die angezeigte Inkonsistenz ergibt sich aus dem Unterschied zwischen dem Code einer Funktion, der die Eigenschaft der Funktion selbst ist, und dem Namen, mit dem sie von einem Modul aufgerufen wird. Dies ist die Eigenschaft des Moduls (Schlüssel in dict). Während der Name einer Funktion ebenfalls im Funktionsobjekt gespeichert wird, wird sie nur für repr verwendet und ist keine grundlegende Eigenschaft der Funktion.

Dies ist durchaus beabsichtigt, da es erlaubt, das gleiche Funktionsobjekt an verschiedenen Stellen unter verschiedenen Namen zu verwenden - oder auch ohne Namen, wenn die Funktion in einem Container gespeichert ist. Dies wäre nicht möglich, wenn man es nur "umbenennen" könnte, indem man eine Eigenschaft der Funktion ändert.

Man kann diese gleiche Unterschied zeigen regelmäßige Python-Funktionen verwenden, wie folgt aus:

>>> def add(a, b): return a + b 
... 
>>> def sub(a, b): return a - b 
... 
>>> add 
<function add at 0x7f9383127938> # the function has a name 
>>> add.__name__ = 'foo' 
>>> add       # the name is changed, but... 
<function foo at 0x7f9383127938> 
>>> foo       # the change doesn't affect the module 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
NameError: name 'foo' is not defined 
>>> add.__code__ = sub.__code__ # we can change the code, though 
>>> add(2, 2) 
0 

Was Ihre Frage im Kommentar: Die Methode Felder nicht kopiert werden, weil Py_InitModule und verwandte Funktionen ausgelegt sind, mit aufgerufen werden statisch zugewiesene Strukturen, deren Kopien eine Raumverschwendung wären. Sie nicht zu kopieren erklärt, warum das Ändern des C Callbacks in ml_meth die Python-Callable ändert.

Verwandte Themen