59. Refcounting

Eine dynamische Sprache wie Python verlangt, dass sich der Rechner darum kümmert, dass Objekte, die nicht mehr gebraucht werden, freigegeben werden. Python erreicht dies üblicherweise über Reference Counts. Dabei hat jedes Objekt einen Zähler, in dem die Zahl der jeweils auf das Objekt gehaltenen Referenzen steht. Wenn die Zahl der Referenzen auf Null geht, wird das Objekt freigegeben.

Wenn man also Python-Werte verwendet, muss man das in der Regel durch einen Aufruf von Py_XINCREF mit dem Objekt als Argument kundtun, wenn man mit dem Objekt fertig ist, muss man Py_XDECREF aufrufen. Das X in diesen Namen steht da, weil es auch noch Varianten ohne X gibt. Der Unterschied ist, dass man den X-Varianten bedenkenlos Nullpointer übergeben kann, was bei den Varianten ohne X zu einem Segfault führt. Dafür sind letztere schneller.

Die meisten Funktionen, die Python-Objekte aus C-Variablen erzeugen, geben bereits ge-INCREF-te Objekte zurück (owned reference), man darf sie also nicht selbst INCREFen. Andere Funktionen geben borrowed references zurück; diese müssen und dürfen wir nicht DECREFen, (wenn wir sie nicht durch INCREFen zu owned references gemacht haben).

In der Python/C API wird jeweils angegeben, ob eine Funktion eine owned oder eine borrowed Reference zurückgibt. Wer C-Module programmiert sollte auf jeden Fall das Refcount-Kapitel in Extending and Embedding gelesen haben.

Zwei Beispiele: Wenn wir einen C-String aus Python zurückgeben wollen, verwenden wir

PyObject *getAString(void)
{
  return PyString_FromString("Ein String");
}

– kein INCREF, weil die Funktion eine New Reference zurückgibt.

Wenn wir ein Element aus einem Tupel zurückgeben wollen, verwenden wir

PyObject *getElement(PyObject *tuple, int index)
{
  PyObject *el=PyTuple_GetItem(tuple, index);

  Py_XINCREF(el);
  return el;
}

– INCREF, weil die Funktion eine Borrowed Reference zurückgibt und wir keine geborgten Referenzen an Python geben wollen.

In diesem Beispiel sieht man auch, warum die X-Varianten verdienstvoll sind. Wen z.B. das GetItem einen IndexError auslöst (was ja gut möglich ist), ist el NULL, ein INCREF würde einen Segfault auslösen. XINCREF tut aber gar nichts, die NULL wird an Python zurückgegeben, eine Exception wird ausgelöst, und GetItem hat vermutlich sogar den richtigen Fehlerstring gesetzt: Alles wunderbar.


Markus Demleitner

Copyright Notice