2. Definiera typer av tillägg: Handledning¶
Python tillåter skribenten av en C-tilläggsmodul att definiera nya typer som kan manipuleras från Python-kod, ungefär som de inbyggda typerna str
och list
. Koden för alla tilläggstyper följer ett mönster, men det finns några detaljer som du behöver förstå innan du kan komma igång. Detta dokument är en lätt introduktion till ämnet.
2.1. Grunderna¶
Körningsprogrammet CPython ser alla Python-objekt som variabler av typen PyObject*, som fungerar som en ”bastyp” för alla Python-objekt. Själva PyObject
-strukturen innehåller bara objektets referensantal och en pekare till objektets ”typobjekt”. Det är här det händer; typobjektet avgör vilka (C)-funktioner som anropas av tolken när t.ex. ett attribut slås upp på ett objekt, en metod anropas eller det multipliceras med ett annat objekt. Dessa C-funktioner kallas ”typmetoder”.
Om du vill definiera en ny tilläggstyp måste du alltså skapa ett nytt typobjekt.
Den här typen av saker kan bara förklaras med exempel, så här är en minimal, men komplett, modul som definierar en ny typ med namnet Custom
inuti en C-tilläggsmodul custom
:
Anteckning
Det vi visar här är det traditionella sättet att definiera statiska tilläggstyper. Det bör vara tillräckligt för de flesta användningsområden. C API tillåter också att definiera heap-allokerade tilläggstyper med hjälp av funktionen PyType_FromSpec()
, som inte behandlas i denna handledning.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
typedef struct {
PyObject_HEAD
/* Type-specific fields go here. */
} CustomObject;
static PyTypeObject CustomType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Custom",
.tp_doc = PyDoc_STR("Custom objects"),
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
};
static int
custom_module_exec(PyObject *m)
{
if (PyType_Ready(&CustomType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot custom_module_slots[] = {
{Py_mod_exec, custom_module_exec},
// Just use this while using static types
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
{0, NULL}
};
static PyModuleDef custom_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "custom",
.m_doc = "Example module that creates an extension type.",
.m_size = 0,
.m_slots = custom_module_slots,
};
PyMODINIT_FUNC
PyInit_custom(void)
{
return PyModuleDef_Init(&custom_module);
}
Nu är det en hel del att ta in på en gång, men förhoppningsvis kommer bitar att verka bekanta från föregående kapitel. Den här filen definierar tre saker:
Vad ett
Custom
objekt innehåller: detta är strukturenCustomObject
, som allokeras en gång för varjeCustom
-instans.Hur
Custom
type beter sig: detta ärCustomType
-strukturen, som definierar en uppsättning flaggor och funktionspoängare som tolken inspekterar när specifika operationer begärs.Hur man definierar och kör modulen
custom
: detta är funktionenPyInit_custom
och den tillhörande strukturencustom_module
för att definiera modulen, samt funktionencustom_module_exec
för att skapa ett nytt modulobjekt.
Den första biten är:
typedef struct {
PyObject_HEAD
} Anpassat objekt;
Det här är vad ett Custom-objekt kommer att innehålla. PyObject_HEAD
är obligatoriskt i början av varje objektstruktur och definierar ett fält som heter ob_base
av typen PyObject
, som innehåller en pekare till ett typobjekt och ett referensantal (dessa kan nås med hjälp av makrot Py_TYPE
respektive Py_REFCNT
). Anledningen till makrot är att abstrahera bort layouten och att möjliggöra ytterligare fält i debug builds.
Anteckning
Det finns inget semikolon ovan efter makrot PyObject_HEAD
. Var försiktig med att lägga till ett av misstag: vissa kompilatorer kommer att klaga.
Naturligtvis lagrar objekt i allmänhet ytterligare data utöver standard PyObject_HEAD
; här är till exempel definitionen för standard Python-flottor:
typedef struct {
PyObjekt_Huvud
dubbel ob_fval;
} PyFloatObject;
Den andra biten är definitionen av typen objekt.
static PyTypeObject CustomType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom.Custom",
.tp_doc = PyDoc_STR("Anpassade objekt"),
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_new = PyType_GenericNew,
};
Anteckning
Vi rekommenderar att man använder C99-stil för angivna initialiserare enligt ovan, för att undvika att lista alla PyTypeObject
-fält som man inte bryr sig om och även för att undvika att bry sig om fältens deklarationsordning.
Den faktiska definitionen av PyTypeObject
i object.h
har många fler fält än definitionen ovan. De återstående fälten kommer att fyllas med nollor av C-kompilatorn, och det är vanligt att inte ange dem explicit om du inte behöver dem.
Vi kommer att plocka isär det, ett fält i taget:
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
Denna rad är en obligatorisk boilerplate för att initiera fältet ob_base
som nämns ovan.
.tp_name = "custom.Custom",
Namnet på vår typ. Detta kommer att visas i standardtextrepresentationen av våra objekt och i vissa felmeddelanden, till exempel:
>>> "" + custom.Custom()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate str (not "custom.Custom") to str
Observera att namnet är ett punktformat namn som innehåller både modulnamnet och namnet på typen i modulen. Modulen i det här fallet är custom
och typen är Custom
, så vi sätter typnamnet till custom.Custom
. Att använda den riktiga prickade importvägen är viktigt för att göra din typ kompatibel med modulerna pydoc
och pickle
.
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
Detta för att Python ska veta hur mycket minne som ska allokeras när nya Custom
-instanser skapas. tp_itemsize
används endast för objekt med variabel storlek och ska annars vara noll.
Anteckning
Om du vill att din typ ska vara subklassbar från Python, och din typ har samma tp_basicsize
som sin bastyp, kan du få problem med multipelt arv. En Python-subklass av din typ måste lista din typ först i sin __bases__
, annars kommer den inte att kunna anropa din typs __new__()
-metod utan att få ett fel. Du kan undvika det här problemet genom att se till att din typ har ett större värde för tp_basicsize
än vad dess bastyp har. För det mesta kommer detta att vara sant ändå, eftersom antingen din bastyp kommer att vara object
, eller så kommer du att lägga till datamedlemmar till din bastyp och därför öka dess storlek.
Vi sätter klassflaggorna till Py_TPFLAGS_DEFAULT
.
.tp_flags = Py_TPFLAGS_DEFAULT,
Alla typer bör inkludera denna konstant i sina flaggor. Den aktiverar alla medlemmar som definierats fram till åtminstone Python 3.3. Om du behöver ytterligare medlemmar måste du OR motsvarande flaggor.
Vi tillhandahåller en dokumentsträng för typen i tp_doc
.
.tp_doc = PyDoc_STR("Anpassade objekt"),
För att möjliggöra skapandet av objekt måste vi tillhandahålla en tp_new
-hanterare. Detta är motsvarigheten till Python-metoden __new__()
, men måste specificeras explicit. I det här fallet kan vi bara använda standardimplementeringen som tillhandahålls av API-funktionen PyType_GenericNew()
.
.tp_new = PyType_GenericNew,
Allt annat i filen bör vara bekant, förutom en del kod i custom_module_exec()
:
if (PyType_Ready(&CustomType) < 0) {
returnera -1;
}
Detta initierar typen Custom
och fyller i ett antal medlemmar med lämpliga standardvärden, inklusive ob_type
som vi ursprungligen satte till NULL
.
if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) {
return -1;
}
Detta lägger till typen i modulens ordlista. Detta gör att vi kan skapa Custom
-instanser genom att anropa Custom
-klassen:
>>> import custom
>>> mycustom = custom.Custom()
Nu är det klart! Allt som återstår är att bygga den; lägg ovanstående kod i en fil som heter custom.c
,
[byggsystem]
kräver = ["setuptools"]
bygg-backend = "setuptools.build_meta"
[projekt]
namn = "anpassad"
version = "1"
i en fil som heter pyproject.toml
, och
from setuptools import Tillägg, setup
setup(ext_modules=[Tillägg("custom", ["custom.c"]))])
i en fil som heter setup.py
; sedan skriver du
$ python -m pip install .
i ett skal bör producera en fil custom.so
i en underkatalog och installera den; starta nu Python — du bör kunna import custom
och leka med Custom
-objekt.
Det var väl inte så svårt, eller hur?
Naturligtvis är den nuvarande Custom-typen ganska ointressant. Den har inga data och gör ingenting. Den kan inte ens underklassificeras.
2.2. Lägga till data och metoder i Basic-exemplet¶
Låt oss utöka grundexemplet för att lägga till data och metoder. Låt oss också göra typen användbar som basklass. Vi skapar en ny modul, custom2
, som lägger till dessa funktioner:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
PyObject *first; /* first name */
PyObject *last; /* last name */
int number;
} CustomObject;
static void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free(self);
}
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->first == NULL) {
Py_DECREF(self);
return NULL;
}
self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->last == NULL) {
Py_DECREF(self);
return NULL;
}
self->number = 0;
}
return (PyObject *) self;
}
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
&first, &last,
&self->number))
return -1;
if (first) {
Py_XSETREF(self->first, Py_NewRef(first));
}
if (last) {
Py_XSETREF(self->last, Py_NewRef(last));
}
return 0;
}
static PyMemberDef Custom_members[] = {
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
"first name"},
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
"last name"},
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
return NULL;
}
if (self->last == NULL) {
PyErr_SetString(PyExc_AttributeError, "last");
return NULL;
}
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Custom_methods[] = {
{"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
};
static PyTypeObject CustomType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom2.Custom",
.tp_doc = PyDoc_STR("Custom objects"),
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = Custom_new,
.tp_init = Custom_init,
.tp_dealloc = Custom_dealloc,
.tp_members = Custom_members,
.tp_methods = Custom_methods,
};
static int
custom_module_exec(PyObject *m)
{
if (PyType_Ready(&CustomType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot custom_module_slots[] = {
{Py_mod_exec, custom_module_exec},
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
{0, NULL}
};
static PyModuleDef custom_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "custom2",
.m_doc = "Example module that creates an extension type.",
.m_size = 0,
.m_slots = custom_module_slots,
};
PyMODINIT_FUNC
PyInit_custom2(void)
{
return PyModuleDef_Init(&custom_module);
}
Denna version av modulen har ett antal ändringar.
Typen Custom
har nu tre dataattribut i sin C-struktur, first, last och number. Variablerna first och last är Python-strängar som innehåller för- och efternamn. Attributet number är ett heltal i C.
Objektstrukturen uppdateras i enlighet med detta:
typedef struct {
PyObject_Huvud
PyObject *first; /* förnamn */
PyObject *last; /* efternamn */
int nummer;
} CustomObject;
Eftersom vi nu har data att hantera måste vi vara mer försiktiga med objektallokering och -deallokering. Vi behöver åtminstone en metod för avallokering:
statiskt void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_XDECREF(själv>första);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free(self);
}
som är tilldelad tp_dealloc
member:
.tp_dealloc = Anpassad_dealloc,
Denna metod rensar först referensräkningarna för de två Python-attributen. Py_XDECREF()
hanterar korrekt fallet där dess argument är NULL
(vilket kan hända här om tp_new
misslyckades halvvägs). Den anropar sedan tp_free
-medlemmen av objektets typ (beräknad av Py_TYPE(self)
) för att frigöra objektets minne. Observera att objektets typ kanske inte är CustomType
, eftersom objektet kan vara en instans av en underklass.
Anteckning
Den uttryckliga castningen till CustomObject *
ovan behövs eftersom vi definierade Custom_dealloc
att ta ett PyObject *
argument, eftersom tp_dealloc
funktionspekaren förväntar sig att ta emot ett PyObject *
argument. Genom att tilldela tp_dealloc
slot till en typ, deklarerar vi att den endast kan anropas med instanser av vår CustomObject
klass, så castet till (CustomObject *)
är säkert. Detta är objektorienterad polymorfism, i C!
I befintlig kod, eller i tidigare versioner av denna handledning, kan du se liknande funktioner som tar en pekare till subtypens objektstruktur (CustomObject*
) direkt, så här:
Custom_dealloc(CustomObject *self)
{
Py_XDECREF(själv>första);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free((PyObject *) self);
}
...
.tp_dealloc = (destructor) Custom_dealloc,
Detta gör samma sak på alla arkitekturer som CPython stöder, men enligt C-standarden ger det upphov till ett odefinierat beteende.
Vi vill se till att för- och efternamn initialiseras till tomma strängar, så vi tillhandahåller en tp_new
-implementering:
statiskt PyObject *
Custom_new(PyTypeObject *typ, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->first = PyUnicode_FromString("");
if (self->first == NULL) {
Py_DECREF(self);
returnera NULL;
}
self->last = PyUnicode_FromString("");
if (self->last == NULL) {
Py_DECREF(self);
returnera NULL;
}
self->number = 0;
}
return (PyObject *) self;
}
och installera den i tp_new
member:
.tp_new = Anpassad_ny,
Handteraren tp_new
ansvarar för att skapa (i motsats till initialisera) objekt av typen. Den exponeras i Python som metoden __new__()
. Det är inte nödvändigt att definiera en medlem tp_new
, och många tilläggstyper återanvänder helt enkelt PyType_GenericNew()
som i den första versionen av typen Custom
ovan. I det här fallet använder vi hanteraren tp_new
för att initialisera attributen first
och last
till standardvärden som inte är NULL
.
tp_new
får den typ som instansieras (inte nödvändigtvis CustomType
, om en underklass instansieras) och alla argument som skickades när typen anropades, och förväntas returnera den skapade instansen. tp_new
-hanterare accepterar alltid positions- och nyckelordsargument, men de ignorerar ofta argumenten och överlåter argumenthanteringen till initialiseringsmetoder (även kallade tp_init
i C eller __init__
i Python).
Anteckning
tp_new
bör inte anropa tp_init
explicit, eftersom tolken kommer att göra det själv.
Implementationen tp_new
anropar tp_alloc
för att allokera minne:
self = (CustomObject *) type->tp_alloc(type, 0);
Eftersom minnesallokering kan misslyckas måste vi kontrollera tp_alloc
-resultatet mot NULL
innan vi fortsätter.
Anteckning
Vi har inte fyllt tp_alloc
-platsen själva. Istället fyller PyType_Ready()
den åt oss genom att ärva den från vår basklass, som är object
som standard. De flesta typer använder standardallokeringsstrategin.
Anteckning
Om du skapar en kooperativ tp_new
(en som anropar en bastyps tp_new
eller __new__()
), får du inte försöka avgöra vilken metod som ska anropas med hjälp av metodresolutionsordning vid körning. Bestäm alltid statiskt vilken typ du ska anropa, och anropa dess tp_new
direkt, eller via type->tp_base->tp_new
. Om du inte gör detta kan det hända att Python-subklasser av din typ som också ärver från andra Python-definierade klasser inte fungerar korrekt. (Specifikt kanske du inte kan skapa instanser av sådana subklasser utan att få ett TypeError
)
Vi definierar också en initialiseringsfunktion som accepterar argument för att tillhandahålla initiala värden för vår instans:
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL, *tmp;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi", kwlist,
&first, &last,
&self->number))
return -1;
if (first) {
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_XDECREF(tmp);
}
if (last) {
tmp = self->last;
Py_INCREF(last);
self->last = last;
Py_XDECREF(tmp);
}
return 0;
}
genom att fylla tp_init
slot.
.tp_init = Custom_init,
Slotten tp_init
exponeras i Python som metoden __init__()
. Den används för att initiera ett objekt efter att det har skapats. Initialiserare accepterar alltid positionella argument och nyckelordsargument, och de bör returnera antingen 0
vid framgång eller -1
vid fel.
Till skillnad från hanteraren tp_new
finns det ingen garanti för att tp_init
anropas överhuvudtaget (till exempel anropar modulen pickle
som standard inte __init__()
på instanser som inte har picklats). Den kan också anropas flera gånger. Vem som helst kan anropa metoden __init__()
på våra objekt. Av denna anledning måste vi vara extra försiktiga när vi tilldelar nya attributvärden. Vi kan till exempel frestas att tilldela medlemmen first
på följande sätt:
if (första) {
Py_XDECREF(själv>först);
Py_INCREF(första);
self->first = första;
}
Men detta skulle vara riskabelt. Vår typ begränsar inte typen av den first
medlemmen, så det kan vara vilken typ av objekt som helst. Den kan ha en destruktor som gör att kod som försöker komma åt den first
medlemmen körs, eller så kan destruktorn koppla bort trådtillstånd och låta godtycklig kod köras i andra trådar som kommer åt och modifierar vårt objekt.
För att vara paranoid och skydda oss mot denna möjlighet omplacerar vi nästan alltid medlemmar innan vi minskar deras referensantal. När behöver vi inte göra detta?
när vi absolut vet att referensantalet är större än 1;
när vi vet att deallokering av objektet [1] varken kommer att ta bort thread state eller orsaka några anrop tillbaka till vår typs kod;
vid dekrementering av ett referensantal i en
tp_dealloc
-hanterare på en typ som inte stöder cyklisk skräpinsamling [2].
Vi vill exponera våra instansvariabler som attribut. Det finns ett antal olika sätt att göra det på. Det enklaste sättet är att definiera medlemsdefinitioner:
static PyMemberDef Custom_members[] = {
{"first", Py_T_OBJECT_EX, offsetof(CustomObject, first), 0,
"förnamn"},
{"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,"efternamn"}, {"last", Py_T_OBJECT_EX, offsetof(CustomObject, last), 0,
"efternamn"},
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"anpassat nummer"},
{NULL} /* Sentinel */
};
och placera definitionerna i tp_members
slot:
.tp_members = Anpassade_medlemmar,
Varje medlemsdefinition har ett medlemsnamn, typ, offset, åtkomstflaggor och dokumentationssträng. Se avsnittet Generisk attributhantering nedan för mer information.
En nackdel med detta tillvägagångssätt är att det inte ger något sätt att begränsa de typer av objekt som kan tilldelas Python-attributen. Vi förväntar oss att för- och efternamn ska vara strängar, men alla Python-objekt kan tilldelas. Dessutom kan attributen tas bort genom att C-pekarna sätts till NULL
. Även om vi kan se till att medlemmarna initialiseras till värden som inte är NULL
, kan medlemmarna sättas till NULL
om attributen raderas.
Vi definierar en enda metod, Custom.name()
, som matar ut objektets namn som en sammankoppling av för- och efternamn.
statiskt PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *) op;
if (self->first == NULL) {
PyErr_SetString(PyExc_AttributeError, "first");
returnera NULL;
}
if (self->last == NULL) {
PyErr_SetString(PyExc_AttributeError, "last");
returnera NULL;
}
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
Metoden är implementerad som en C-funktion som tar en instans av Custom
(eller Custom
subclass) som första argument. Metoder tar alltid en instans som första argument. Metoder tar ofta positionella argument och nyckelordsargument också, men i det här fallet tar vi inga och behöver inte acceptera en tupel med positionella argument eller en ordbok med nyckelordsargument. Denna metod är likvärdig med Python-metoden:
def namn(self):
return "%s %s" % (self.first, self.last)
Observera att vi måste kontrollera om våra medlemmar first
och last
är NULL
. Detta beror på att de kan raderas, i vilket fall de sätts till NULL
. Det vore bättre att förhindra radering av dessa attribut och att begränsa attributvärdena till strängar. Vi ska se hur man gör det i nästa avsnitt.
Nu när vi har definierat metoden måste vi skapa en matris med metoddefinitioner:
statisk PyMethodDef Custom_methods[] = {
{"name", Eget_namn, METH_NOARGS,
"Returnera namnet genom att kombinera för- och efternamn"
},
{NULL} /* Sentinel */
};
(notera att vi använde METH_NOARGS
-flaggan för att ange att metoden inte förväntar sig några andra argument än self)
och tilldela den till tp_methods
slot:
.tp_methods = Anpassade_metoder,
Slutligen ska vi göra vår typ användbar som basklass för underklassning. Vi har hittills skrivit våra metoder noggrant så att de inte gör några antaganden om typen av det objekt som skapas eller används, så allt vi behöver göra är att lägga till Py_TPFLAGS_BASETYPE
till vår klassflaggdefinition:
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
Vi byter namn på PyInit_custom()
till PyInit_custom2()
, uppdaterar modulnamnet i struct:en PyModuleDef
och uppdaterar det fullständiga klassnamnet i struct:en PyTypeObject
.
Slutligen uppdaterar vi vår setup.py
-fil så att den inkluderar den nya modulen,
from setuptools import Extension, setup
setup(ext_modules=[
Extension("custom", ["custom.c"]),
Extension("custom2", ["custom2.c"]),
])
och sedan installerar vi om så att vi kan import custom2
:
$ python -m pip install .
2.3. Tillhandahåller finare kontroll över dataattribut¶
I det här avsnittet ska vi ge finare kontroll över hur attributen first
och last
anges i exemplet Custom
. I den tidigare versionen av vår modul kunde instansvariablerna first
och last
sättas till icke-strängvärden eller till och med raderas. Vi vill se till att dessa attribut alltid innehåller strängar.
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
PyObject *first; /* first name */
PyObject *last; /* last name */
int number;
} CustomObject;
static void
Custom_dealloc(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_XDECREF(self->first);
Py_XDECREF(self->last);
Py_TYPE(self)->tp_free(self);
}
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->first == NULL) {
Py_DECREF(self);
return NULL;
}
self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->last == NULL) {
Py_DECREF(self);
return NULL;
}
self->number = 0;
}
return (PyObject *) self;
}
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
&first, &last,
&self->number))
return -1;
if (first) {
Py_SETREF(self->first, Py_NewRef(first));
}
if (last) {
Py_SETREF(self->last, Py_NewRef(last));
}
return 0;
}
static PyMemberDef Custom_members[] = {
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};
static PyObject *
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}
static int
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
return -1;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be a string");
return -1;
}
Py_SETREF(self->first, Py_NewRef(value));
return 0;
}
static PyObject *
Custom_getlast(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}
static int
Custom_setlast(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
return -1;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The last attribute value must be a string");
return -1;
}
Py_SETREF(self->last, Py_NewRef(value));
return 0;
}
static PyGetSetDef Custom_getsetters[] = {
{"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
{"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *) op;
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Custom_methods[] = {
{"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
};
static PyTypeObject CustomType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom3.Custom",
.tp_doc = PyDoc_STR("Custom objects"),
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_new = Custom_new,
.tp_init = Custom_init,
.tp_dealloc = Custom_dealloc,
.tp_members = Custom_members,
.tp_methods = Custom_methods,
.tp_getset = Custom_getsetters,
};
static int
custom_module_exec(PyObject *m)
{
if (PyType_Ready(&CustomType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot custom_module_slots[] = {
{Py_mod_exec, custom_module_exec},
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
{0, NULL}
};
static PyModuleDef custom_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "custom3",
.m_doc = "Example module that creates an extension type.",
.m_size = 0,
.m_slots = custom_module_slots,
};
PyMODINIT_FUNC
PyInit_custom3(void)
{
return PyModuleDef_Init(&custom_module);
}
För att ge större kontroll över attributen first
och last
använder vi anpassade getter- och setter-funktioner. Här är funktionerna för att hämta och ställa in attributet first
:
static PyObject *
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
Py_INCREF(self->first);
return self->first;
}
static int
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
PyObject *tmp;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
return -1;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be a string");
return -1;
}
tmp = self->first;
Py_INCREF(value);
self->first = value;
Py_DECREF(tmp);
return 0;
}
Getter-funktionen får ett Custom
-objekt och en ”closure”, som är en void-pekare. I det här fallet ignoreras closure. (Closure stöder en avancerad användning där definitionsdata skickas till getter- och setterfunktionerna. Detta kan t.ex. användas för att tillåta en enda uppsättning getter- och setterfunktioner som bestämmer vilket attribut som ska hämtas eller sättas baserat på data i closure)
Setter-funktionen får Custom
-objektet, det nya värdet och avslutningen. Det nya värdet kan vara NULL
, i vilket fall attributet tas bort. I vår setter ger vi ett felmeddelande om attributet raderas eller om det nya värdet inte är en sträng.
Vi skapar en array av PyGetSetDef
-strukturer:
statisk PyGetSetDef Custom_getsetters[] = {
{"first", Custom_getfirst, Custom_setfirst,
"förnamn", NULL},
{"last", Custom_getlast, Custom_setlast,
"efternamn", NULL},
{NULL} /* Sentinel */
};
och registrera den i tp_getset
slot:
.tp_getset = Anpassade_getsetters,
Det sista objektet i en PyGetSetDef
-struktur är den ”closure” som nämns ovan. I det här fallet använder vi inte en closure, så vi skickar bara NULL
.
Vi tar också bort medlemsdefinitionerna för dessa attribut:
static PyMemberDef Custom_members[] = {
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"anpassat nummer"},
{NULL} /* Sentinel */
};
Vi måste också uppdatera tp_init
-handlaren så att den bara tillåter strängar [3] att skickas:
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL, *tmp;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
&first, &last,
&self->number))
return -1;
if (first) {
tmp = self->first;
Py_INCREF(first);
self->first = first;
Py_DECREF(tmp);
}
if (last) {
tmp = self->last;
Py_INCREF(last);
self->last = last;
Py_DECREF(tmp);
}
return 0;
}
Med dessa ändringar kan vi säkerställa att medlemmarna first
och last
aldrig är NULL
, så vi kan ta bort kontroller för NULL
-värden i nästan alla fall. Detta innebär att de flesta Py_XDECREF()
-anropen kan konverteras till Py_DECREF()
-anrop. Det enda stället där vi inte kan ändra dessa anrop är i implementeringen av tp_dealloc
, där det finns en risk att initialiseringen av dessa medlemmar misslyckades i tp_new
.
Vi byter också namn på modulinitialiseringsfunktionen och modulnamnet i initialiseringsfunktionen, som vi gjorde tidigare, och vi lägger till en extra definition i filen setup.py
.
2.4. Stöd för cyklisk skräpinsamling¶
Python har en cyclic garbage collector (GC) som kan identifiera onödiga objekt även när deras referensantal inte är noll. Detta kan hända när objekt är involverade i cykler. Tänk till exempel på:
>>> l = []
>>> l.append(l)
>>> del l
I det här exemplet skapar vi en lista som innehåller sig själv. När vi tar bort den har den fortfarande en referens från sig själv. Dess referensantal sjunker inte till noll. Lyckligtvis kommer Pythons cykliska skräpsamlare så småningom att räkna ut att listan är skräp och frigöra den.
I den andra versionen av exemplet Custom
tillät vi att alla typer av objekt lagrades i attributen first
eller last
[4]. Dessutom, i den andra och tredje versionen, tillät vi underklassning av Custom
, och underklasser kan lägga till godtyckliga attribut. Av någon av dessa två anledningar kan Custom
-objekt delta i cykler:
>>> import custom3
>>> class Derived(custom3.Custom): pass
...
>>> n = Derived()
>>> n.some_attribute = n
För att en Custom
-instans som deltar i en referenscykel ska kunna upptäckas och samlas in på rätt sätt av den cykliska GC:n, måste vår Custom
-typ fylla två ytterligare slots och aktivera en flagga som aktiverar dessa slots:
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stddef.h> /* for offsetof() */
typedef struct {
PyObject_HEAD
PyObject *first; /* first name */
PyObject *last; /* last name */
int number;
} CustomObject;
static int
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *) op;
Py_VISIT(self->first);
Py_VISIT(self->last);
return 0;
}
static int
Custom_clear(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
}
static void
Custom_dealloc(PyObject *op)
{
PyObject_GC_UnTrack(op);
(void)Custom_clear(op);
Py_TYPE(op)->tp_free(op);
}
static PyObject *
Custom_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
CustomObject *self;
self = (CustomObject *) type->tp_alloc(type, 0);
if (self != NULL) {
self->first = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->first == NULL) {
Py_DECREF(self);
return NULL;
}
self->last = Py_GetConstant(Py_CONSTANT_EMPTY_STR);
if (self->last == NULL) {
Py_DECREF(self);
return NULL;
}
self->number = 0;
}
return (PyObject *) self;
}
static int
Custom_init(PyObject *op, PyObject *args, PyObject *kwds)
{
CustomObject *self = (CustomObject *) op;
static char *kwlist[] = {"first", "last", "number", NULL};
PyObject *first = NULL, *last = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|UUi", kwlist,
&first, &last,
&self->number))
return -1;
if (first) {
Py_SETREF(self->first, Py_NewRef(first));
}
if (last) {
Py_SETREF(self->last, Py_NewRef(last));
}
return 0;
}
static PyMemberDef Custom_members[] = {
{"number", Py_T_INT, offsetof(CustomObject, number), 0,
"custom number"},
{NULL} /* Sentinel */
};
static PyObject *
Custom_getfirst(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->first);
}
static int
Custom_setfirst(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the first attribute");
return -1;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The first attribute value must be a string");
return -1;
}
Py_XSETREF(self->first, Py_NewRef(value));
return 0;
}
static PyObject *
Custom_getlast(PyObject *op, void *closure)
{
CustomObject *self = (CustomObject *) op;
return Py_NewRef(self->last);
}
static int
Custom_setlast(PyObject *op, PyObject *value, void *closure)
{
CustomObject *self = (CustomObject *) op;
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "Cannot delete the last attribute");
return -1;
}
if (!PyUnicode_Check(value)) {
PyErr_SetString(PyExc_TypeError,
"The last attribute value must be a string");
return -1;
}
Py_XSETREF(self->last, Py_NewRef(value));
return 0;
}
static PyGetSetDef Custom_getsetters[] = {
{"first", Custom_getfirst, Custom_setfirst,
"first name", NULL},
{"last", Custom_getlast, Custom_setlast,
"last name", NULL},
{NULL} /* Sentinel */
};
static PyObject *
Custom_name(PyObject *op, PyObject *Py_UNUSED(dummy))
{
CustomObject *self = (CustomObject *) op;
return PyUnicode_FromFormat("%S %S", self->first, self->last);
}
static PyMethodDef Custom_methods[] = {
{"name", Custom_name, METH_NOARGS,
"Return the name, combining the first and last name"
},
{NULL} /* Sentinel */
};
static PyTypeObject CustomType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "custom4.Custom",
.tp_doc = PyDoc_STR("Custom objects"),
.tp_basicsize = sizeof(CustomObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
.tp_new = Custom_new,
.tp_init = Custom_init,
.tp_dealloc = Custom_dealloc,
.tp_traverse = Custom_traverse,
.tp_clear = Custom_clear,
.tp_members = Custom_members,
.tp_methods = Custom_methods,
.tp_getset = Custom_getsetters,
};
static int
custom_module_exec(PyObject *m)
{
if (PyType_Ready(&CustomType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "Custom", (PyObject *) &CustomType) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot custom_module_slots[] = {
{Py_mod_exec, custom_module_exec},
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
{0, NULL}
};
static PyModuleDef custom_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "custom4",
.m_doc = "Example module that creates an extension type.",
.m_size = 0,
.m_slots = custom_module_slots,
};
PyMODINIT_FUNC
PyInit_custom4(void)
{
return PyModuleDef_Init(&custom_module);
}
För det första låter traversalmetoden den cykliska GC:n veta om underobjekt som kan delta i cykler:
static int
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *) op;
int vret;
if (self->first) {
vret = visit(self->first, arg);
if (vret != 0)
return vret;
}
if (self->last) {
vret = visit(self->last, arg);
if (vret != 0)
return vret;
}
return 0;
}
För varje subobjekt som kan delta i cykler måste vi anropa funktionen visit()
, som skickas till traversalmetoden. Funktionen visit()
tar som argument subobjektet och det extra argumentet arg som skickas till traversalmetoden. Den returnerar ett heltalsvärde som måste returneras om det inte är noll.
Python tillhandahåller ett Py_VISIT()
-makro som automatiserar anrop av visitfunktioner. Med Py_VISIT()
kan vi minimera mängden boilerplate i Custom_traverse
:
static int
Custom_traverse(PyObject *op, visitproc visit, void *arg)
{
CustomObject *self = (CustomObject *) op;
Py_VISIT(self->first);
Py_VISIT(self->last);
return 0;
}
Anteckning
Implementationen tp_traverse
måste namnge sina argument exakt visit och arg för att kunna använda Py_VISIT()
.
För det andra måste vi tillhandahålla en metod för att rensa alla underobjekt som kan delta i cycles:
static int
Custom_clear(PyObject *op)
{
CustomObject *self = (CustomObject *) op;
Py_CLEAR(self->first);
Py_CLEAR(self->last);
return 0;
}
Lägg märke till användningen av makrot Py_CLEAR()
. Det är det rekommenderade och säkra sättet att rensa dataattribut av godtyckliga typer samtidigt som deras referensantal dekrementeras. Om du istället skulle anropa Py_XDECREF()
på attributet innan du sätter det till NULL
, finns det en möjlighet att attributets destruktor skulle anropa tillbaka till kod som läser attributet igen (särskilt om det finns en referenscykel).
Anteckning
Du kan emulera Py_CLEAR()
genom att skriva:
PyObject *tmp;
tmp = själv>första;
self->first = NULL;
Py_XDECREF(tmp);
Det är dock mycket enklare och mindre felbenäget att alltid använda Py_CLEAR()
när man tar bort ett attribut. Försök inte mikrooptimera på bekostnad av robusthet!
Deallokatorn Custom_dealloc
kan anropa godtycklig kod när attribut rensas. Det betyder att den cirkulära GC kan utlösas inuti funktionen. Eftersom GC antar att referensantalet inte är noll måste vi spåra objektet från GC genom att anropa PyObject_GC_UnTrack()
innan vi rensar medlemmar. Här är vår reimplementerade deallocator som använder PyObject_GC_UnTrack()
och Custom_clear
:
statiskt void
Custom_dealloc(PyObject *op)
{
PyObject_GC_UnTrack(op);
(void)Custom_clear(op);
Py_TYPE(op)->tp_free(op);
}
Slutligen lägger vi till flaggan Py_TPFLAGS_HAVE_GC
i klassen flags:
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
Det är i stort sett allt. Om vi hade skrivit egna tp_alloc
- eller tp_free
-hanterare, skulle vi behöva modifiera dem för cyklisk skräpinsamling. De flesta tillägg kommer att använda de versioner som automatiskt tillhandahålls.
2.5. Underklassificering av andra typer¶
Det är möjligt att skapa nya tilläggstyper som härrör från befintliga typer. Det är lättast att ärva från de inbyggda typerna, eftersom ett tillägg enkelt kan använda de PyTypeObject
som det behöver. Det kan vara svårt att dela dessa PyTypeObject
-strukturer mellan tilläggsmoduler.
I det här exemplet skapar vi en SubList
-typ som ärver från den inbyggda list
-typen. Den nya typen kommer att vara helt kompatibel med vanliga listor, men kommer att ha en ytterligare increment()
-metod som ökar en intern räknare:
>>> import sublist
>>> s = sublist.SubList(range(3))
>>> s.extend(s)
>>> print(len(s))
6
>>> print(s.increment())
1
>>> print(s.increment())
2
#define PY_SSIZE_T_CLEAN
#include <Python.h>
typedef struct {
PyListObject list;
int state;
} SubListObject;
static PyObject *
SubList_increment(PyObject *op, PyObject *Py_UNUSED(dummy))
{
SubListObject *self = (SubListObject *) op;
self->state++;
return PyLong_FromLong(self->state);
}
static PyMethodDef SubList_methods[] = {
{"increment", SubList_increment, METH_NOARGS,
PyDoc_STR("increment state counter")},
{NULL},
};
static int
SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
SubListObject *self = (SubListObject *) op;
if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
}
static PyTypeObject SubListType = {
.ob_base = PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "sublist.SubList",
.tp_doc = PyDoc_STR("SubList objects"),
.tp_basicsize = sizeof(SubListObject),
.tp_itemsize = 0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_init = SubList_init,
.tp_methods = SubList_methods,
};
static int
sublist_module_exec(PyObject *m)
{
SubListType.tp_base = &PyList_Type;
if (PyType_Ready(&SubListType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) {
return -1;
}
return 0;
}
static PyModuleDef_Slot sublist_module_slots[] = {
{Py_mod_exec, sublist_module_exec},
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED},
{0, NULL}
};
static PyModuleDef sublist_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "sublist",
.m_doc = "Example module that creates an extension type.",
.m_size = 0,
.m_slots = sublist_module_slots,
};
PyMODINIT_FUNC
PyInit_sublist(void)
{
return PyModuleDef_Init(&sublist_module);
}
Som du kan se är källkoden mycket lik Custom
-exemplen i tidigare avsnitt. Vi kommer att bryta ner de viktigaste skillnaderna mellan dem.
typedef struct {
PyListObject lista;
int tillstånd;
} SubListObject;
Den primära skillnaden för objekt av härledd typ är att objektstrukturen för bastypen måste vara det första värdet. Bastypen kommer redan att innehålla PyObject_HEAD()
i början av sin struktur.
När ett Python-objekt är en SubList
-instans kan dess PyObject *
-pekare säkert omvandlas till både PyListObject *
och SubListObject *
:
static int
SubList_init(PyObject *op, PyObject *args, PyObject *kwds)
{
SubListObject *self = (SubListObject *) op;
if (PyList_Type.tp_init(op, args, kwds) < 0)
return -1;
self->state = 0;
return 0;
}
Vi ser ovan hur man anropar till __init__()
-metoden för bastypen.
Detta mönster är viktigt när man skriver en typ med anpassade tp_new
och tp_dealloc
-medlemmar. Hanteraren tp_new
ska inte skapa minnet för objektet med sin tp_alloc
, utan låta basklassen hantera det genom att anropa sin egen tp_new
.
Strukturen PyTypeObject
stöder en tp_base
som anger typens konkreta basklass. På grund av kompilatorproblem mellan plattformar kan du inte fylla i det fältet direkt med en referens till PyList_Type
; det bör göras i funktionen Py_mod_exec
:
static int
sublist_module_exec(PyObject *m)
{
SubListType.tp_base = &PyList_Type;
if (PyType_Ready(&SubListType) < 0) {
return -1;
}
if (PyModule_AddObjectRef(m, "SubList", (PyObject *) &SubListType) < 0) {
return -1;
}
return 0;
}
Innan PyType_Ready()
anropas måste typstrukturen ha tp_base
slot ifylld. När vi härleder en befintlig typ är det inte nödvändigt att fylla i tp_alloc
med PyType_GenericNew()
– allokeringsfunktionen från bastypen kommer att ärvas.
Därefter anropas PyType_Ready()
och typobjektet läggs till i modulen på samma sätt som i de grundläggande Custom
-exemplen.
Fotnoter