Da ich mir im letzten Jahr Sorgen um "Python-ness" gemacht hatte, las ich die Implementierung von "range".
Verwenden wir "xrange" anstelle von "range"! Es war eine super Entgleisung aus der Geschichte der Python 2-Ära.
Repository: https://hg.python.org/cpython Spiegeln: https://github.com/python/cpython (Ich bin mir bei Mercurial nicht sicher, daher sind alle folgenden Git)
https://github.com/python/cpython/tree/master/Objects Es gibt verschiedene interessante Dinge in der Linie. Alle von ihnen sind lang und unlesbar.
Source files for various builtin objects
Es scheint, dass es eine Geschichte gab, in der range
und xrange
in Python2 unterschiedlich sind. Es scheint, dass Python 3 sich darüber keine Sorgen machen muss.
Es scheint, dass es etwas gibt, das [2to3] heißt (http://docs.python.jp/3.5/library/2to3.html), und es gibt auch ein Programm, das "xrange" ordnungsgemäß in "range" konvertiert.
https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_xrange.py
Diesmal https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Objects/rangeobject.c Ich lese. Es gibt 1246 Zeilen, aber der Schwierigkeitsgrad ist wahrscheinlich niedrig.
3〜4: include
Die Header-Datei ist https://github.com/python/cpython/tree/master/Include Es ist in.
Was in rangeobject.c zu lesen ist
typedef struct PyMemberDef
Zwei von.
Das Array von PyMemberDef
scheint die Namen, Typen und Offsets der Mitglieder der C-Struktur zu definieren.
rangeobject
6〜19: struct rangeobject
Das Strukturbereichsobjekt
haben. Es sieht aus wie eine Reichweite.
PyObject_HEAD
und PyObject
werden in Common Object Structures und allen Objekttypen beschrieben Ist eine Erweiterung von PyObject
.
Der Wert PY_SSIZE_T_MAX wird im Kommentar angezeigt. Py_ssize_t
ist eine vorzeichenbehaftete Ganzzahl im Gegensatz zu ssize_t
. ssize_t
wird in PEP 353 - Verwenden von ssize_t als Indextyp | Python.org erwähnt.
21〜39: validate_step(PyObject *step)
Hilfsfunktion für Verifizierungsschritt. Schritt 0 ist nicht als Bereich geeignet, daher scheint er ihn zu überprüfen. Wenn kein Schritt angegeben ist, wird er als 1 betrachtet.
41〜42: compute_range_length(PyObject *start, PyObject *stop, PyObject *step)
→ Zeile 151
44〜64: make_range_object(PyTypeObject *type, PyObject *start, PyObject *stop, PyObject *step)
Erstellen Sie aus dem Argument ein Bereichsobjekt. Es ist das folgende range_new, das wirklich rangeobject
erstellt, und wenn es kein Problem gibt, nachdem verschiedene Dinge mit range_new überprüft wurden, wird make_range_object aufgerufen.
Da rangeobject
ein Objekt ist, ist es eine Erweiterung von PyObject
. Geben Sie nach dem Erstellen von "PyObject" den Wert des Bereichs (Start usw.) an.
Was ist PyObject_New (Typ, Typobj)? (Typ *) _PyObject_New (Typobj)? -L135), wodurch "PyObject" durch Zuweisen von Speicher erstellt wird. Weitere Informationen finden Sie unter object.c. Abkürzung.
66〜129: range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
Machen Sie verschiedene Überprüfungen richtig und machen Sie "RangeObject". Wenn ein unangemessener Wert übergeben wird, geben Sie die Erstellung auf, weisen Sie den zugewiesenen Speicher zu und geben Sie "NULL" zurück. Es gab einen Kommentar wie diesen.
/* XXX(nnorwitz): should we error check if the user passes any empty ranges?
range(-10)
range(0, -5)
range(0, 5, -1)
*/
131〜139: PyDoc
Dokumente für Bereich (Stopp) und Bereich (Start, Stopp [, Schritt]) werden mit einem Makro namens PyDoc_STRVAR geschrieben. PyDoc_STRVAR
#define PyDoc_VAR(name) static char name[]
Ankommen in. Es ist ein Makro für Inline-Dokumente.
141〜149: range_dealloc(rangeobject *r)
Freigabe von Mitgliedern von struct rangeobject
→ Freigabe von Struktur.
151〜227: compute_range_length(PyObject *start, PyObject *stop, PyObject *step)
Berechnet die Anzahl der Elemente im Bereich (lo, hi, step) und gibt sie zurück. Der Algorithmus scheint der gleiche zu sein wie get_len_of_range (lo, hi, step) ab Zeile 865, und die detaillierte Geschichte ist dort geschrieben. Der Unterschied besteht darin, dass das Argument "PyObject" ist und dass "PyObject" als "PyLong" angesehen wird und die Geschichte weitergeht.
229〜233: range_length(rangeobject *r)
Gibt die Elementlänge als Py_ssize_t
zurück.
Das hier verwendete PyLong_AsSsize_t scheint "Py_ssize_t" von einem Objekt vom Typ int zurückzugeben.
235〜248: compute_item(rangeobject *r, PyObject *i)
Berechnen Sie den Wert von Anfang, i und Schritt und erhalten Sie den nächsten Wert. Der Kommentar sagt auch "return r-> start + (i * r-> step)". Wie es ist.
250〜307: compute_range_item(rangeobject *r, PyObject *arg)
Diejenigen, die das obige compute_item aufrufen. Vorher wird i von compute_item (rangeobject * r, PyObject * i) von arg ausgegeben (tatsächlich scheint es "PyLong" zu sein) und der Bereich wird überprüft.
309〜319: range_item(rangeobject *r, Py_ssize_t i)
Die Seite, die das obige compute_range_item aufruft. Dies kümmert sich um Funktionsargumente und Referenzen.
321〜358: compute_slice(rangeobject *r, PyObject *_slice)
Machen Sie ein Stück "RangeObject". Es behandelt auch Fehler und gibt Referenzen frei.
360〜409: range_contains_long(rangeobject *r, PyObject *ob)
In den nächsten range_contains, range_index, range_count
PyLong_CheckExact(ob) || PyBool_Check(ob)
Wird mit dem Häkchen ausgeführt. PyLong_CheckExact und PyBool_Check /boolobject.h#L12) ist auch ein Makro, das eine Typprüfung mit derselben Bedeutung durchführt.
Liegt der Inhalt einfach innerhalb des vom Bereichsobjekt angegebenen Bereichs (= existiert er)?
411〜419: range_contains(rangeobject *r, PyObject *ob)
(PyLong_CheckExact(ob) || PyBool_Check(ob))
Dann der obige Bereich_contains_Andernfalls lange verwenden_PySequence_IterSearchMachen Sie ein Urteil mit.
421〜465: range_equals(rangeobject *r0, rangeobject *r1)
Eine Funktion, die zwei Range-Objekte vergleicht. In einem Kommentar wird erläutert, wo gesucht werden muss, um eine Entscheidung zu treffen.
/*
if r0 is r1:
return True
if len(r0) != len(r1):
return False
if not len(r0):
return True
if r0.start != r1.start:
return False
if len(r0) == 1:
return True
return r0.step == r1.step
*/
467〜495: range_richcompare(PyObject *self, PyObject *other, int op)
Das Argument lautet "PyObject", aber es gibt eine Überprüfung, um festzustellen, ob andere "PyRange_Type" ist. op verweist auf den Vergleichsoperator (https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Include/object.h#L927), und diese Funktion zielt auf "Py_NE" und "Py_EQ" ab nur.
Rufen Sie schließlich range_equals auf.
497〜550: range_hash(rangeobject *r)
Hash-Funktion für rangeobject
. Kehren Sie mit dem Typ [Py_hash_t] zurück (https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Include/pyport.h#L93). Die Werte, die zum Abrufen des Hashs verwendet werden, sind len (), start und step. Die Verarbeitung wird entsprechend dem Wert von len (r) geändert.
/* Hash function for range objects. Rough C equivalent of
if not len(r):
return hash((len(r), None, None))
if len(r) == 1:
return hash((len(r), r.start, None))
return hash((len(r), r.start, r.step))
*/
552〜570: range_count(rangeobject *r, PyObject *ob)
PyLong_CheckExact(ob) || PyBool_Check(ob)
Zum Zeitpunkt der Reichweite_contains_sonst lang_PySequence_Zählen Sie mit IterSearch.
572〜602: range_index(rangeobject *r, PyObject *ob)
PyLong_CheckExact(ob) || PyBool_Check(ob)
Zum Zeitpunkt der Reichweite_contains_sonst lang_PySequence_Suchen Sie mit IterSearch.
Für range_contains_long nach Überprüfung auf Existenz
idx = PyNumber_FloorDivide(tmp, r->step);
Der Standort wird berechnet und gelöscht.
604〜613: range_as_sequence
Registrieren Sie verschiedene Funktionen gemäß PySequenceMethods. Hier
PySequenceMethods |
range_as_sequence |
---|---|
lenfunc sq_length |
range_Länge → Zeile 229 |
ssizeargfunc sq_item |
range_Punkt → Zeile 309 |
objobjproc sq_contains |
range_enthält → Zeile 411 |
Nur sind registriert.
615〜633: range_repr(rangeobject *r)
Geben Sie die Ausgabezeichenfolge in PyUnicode aus. Wenn Schritt 1 ist, wird Schritt weggelassen.
635〜641: range_reduce(rangeobject *r, PyObject *args)
Es scheint eine Hilfsfunktion von [pickle] zu sein (https://docs.python.org/3.6/library/pickle.html). Py_BuildValue ([\ _Py_BuildValue_Size/p). Geben Sie die Informationen zu struct rangeobject
an Python / modsupport.c # L441-L450)).
643〜662: range_subscript(rangeobject* self, PyObject* item)
Artikel ist Wenn Sie PyIndex_Check durchgehen (kurz, int), können Sie comp_ verwenden. Wenn Sie PySlice_Check durchgehen (im Wesentlichen Slice), können Sie compute_slice verwenden. Gibt das Element zurück. Ansonsten ist es ein Fehler.
665〜669: range_as_mapping
Dieses Mal wird es gemäß PyMappingMethods registriert. 2 von 3 Funktionen sind implementiert.
PyMappingMethods |
range_as_mapping |
---|---|
lenfunc mp_length |
range_Länge → Zeile 229 |
binaryfunc mp_subscript |
range_Index → Zeile 643 |
671: range_iter(PyObject *seq)
→ Zeile 1076
672: range_reverse
→ Zeile 1134
674〜682: PyDoc
Dokumente für Reverse, Count und Index.
684〜690: range_methods
PyMethodDef verknüpft Funktionen in Python mit in C implementierten Funktionen, Argumentinformationen und Dokumentation. Ding.
Hier sind folgende Funktionen verknüpft.
Eingebaut | Implementierung in C. |
---|---|
__reversed__ | range_reverse →1134 |
__reduce__ | range_reduce →635 |
count | range_count →552 |
index | range_index →572 |
692〜697: range_members
PyMemberDef repräsentiert den Namen, den Typ und den Versatz der Mitglieder der C-Struktur. Hier handelt es sich um eine Reihe von Start-, Stopp- und Schrittinformationen.
699〜738: PyRange_Type
Eine Typdefinition namens "PyRange_Type". Es gibt verschiedene Einstellungselemente, aber im Fall des Bereichs werden die folgenden Informationen registriert Es gibt.
PyTypeObject |
PyRange_Type |
---|---|
const char *tp_name |
"range" |
Py_ssize_t tp_basicsize |
sizeof(rangeobject) |
destructor tp_dealloc |
(destructor)range_dealloc →141 |
reprfunc tp_repr |
(reprfunc)range_repr →615 |
PySequenceMethods *tp_as_sequence |
&range_as_sequence →604 |
PyMappingMethods *tp_as_mapping |
&range_as_mapping →665 |
hashfunc tp_hash |
(hashfunc)range_hash →497 |
getattrofunc tp_getattro |
PyObject_GenericGetAttr |
unsigned long tp_flags |
Py_TPFLAGS_DEFAULT |
const char *tp_doc |
range_doc |
richcmpfunc tp_richcompare |
range_richcompare →467 |
getiterfunc tp_iter |
range_iter →1076 |
struct PyMethodDef *tp_methods |
range_methods →684 |
struct PyMemberDef *tp_members |
range_members →692 |
newfunc tp_new |
range_new →66 |
Übrigens scheint PyVarObject_HEAD_INIT mit der Definition des ursprünglichen Segments von "Py" in Zusammenhang zu stehen.
rangeiterobject
740〜753: struct rangeiterobject
Die Definition ähnelt "rangeobject", unterscheidet sich jedoch geringfügig.
755〜764: rangeiter_next(rangeiterobject *r)
Holen Sie sich den nächsten Wert, indem Sie zum Starten den Schritt index * hinzufügen. Um einen Überlauf zu vermeiden, wird es in unsigniert umgewandelt und zurückgegeben.
766〜770: rangeiter_len(rangeiterobject *r)
Berechnen Sie die Länge mit len --index.
772〜773: PyDoc
length_hint_doc. Es scheint eine private Methode zu sein, die eine Schätzung der Länge der Liste liefert.
775〜802: rangeiter_reduce(rangeiterobject *r)
Funktion für Gurke. Es dauert länger als das Bereichsobjekt, da eine Konvertierung in "PyLong" erfolgt.
804〜817: rangeiter_setstate(rangeiterobject *r, PyObject *state)
Stellen Sie den Wert im Index ein.
819〜820: PyDoc
doc für reduzieren und setstate. Beide scheinen mit Gurken zu tun zu haben.
822〜830: rangeiter_methods
Eingebaut | Implementierung in C. |
---|---|
__length_hint__ | rangeiter_len →766 |
__reduce__ | rangeiter_reduce →775 |
__setstate__ | rangeiter_setstate → 804 |
832〜863: PyRangeIter_Type
PyTypeObject |
PyRangeIter_Type |
---|---|
const char *tp_name |
"range_iterator" |
Py_ssize_t tp_basicsize |
sizeof(rangeiterobject) |
destructor tp_dealloc |
(destructor)PyObject_Del |
getattrofunc tp_getattro |
PyObject_GenericGetAttr |
unsigned long tp_flags |
Py_TPFLAGS_DEFAULT |
getiterfunc tp_iter |
PyObject_SelfIter |
iternextfunc tp_iternext |
(iternextfunc)rangeiter_next →755 |
struct PyMethodDef *tp_methods |
rangeiter_methods →822 |
Ich frage mich, warum es PyObject_Del und PyObject_DEL gibt ...
865〜891: get_len_of_range(long lo, long hi, long step)
Gibt die Anzahl der Elemente im Bereich zurück (lo, hi, step).
/* -------------------------------------------------------------
If step > 0 and lo >= hi, or step < 0 and lo <= hi, the range is empty.
Else for step > 0, if n values are in the range, the last one is
lo + (n-1)*step, which must be <= hi-1. Rearranging,
n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so
the RHS is non-negative and so truncation is the same as the
floor. Letting M be the largest positive long, the worst case
for the RHS numerator is hi=M, lo=-M-1, and then
hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough
precision to compute the RHS exactly. The analysis for step < 0
is similar.
---------------------------------------------------------------*/
Die Kommentarbeschreibung ist länger als der Code. Der Punkt teilt nur den Abschnitt durch die Anzahl der Schritte.
893〜915: fast_range_iter(long start, long stop, long step)
Machen Sie einen Entfernungsmesser. Wenn es länger als der lange Typ von C ist, wird NULL als Fehler zurückgegeben.
longrangeiterobject
917〜923: struct longrangeiterobject
Dies ist "PyObject".
925〜929: longrangeiter_len(longrangeiterobject *r, PyObject *no_args)
Verwenden Sie PyNumber_Subtract, um den Index von len zu subtrahieren.
931〜958: longrangeiter_reduce(longrangeiterobject *r)
Für Gurke. Berechnen Sie stop und berechnen Sie den Wert, indem Sie mit make_range_object ein Bereichsobjekt erstellen.
960〜987: longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
Status in Index setzen.
989〜997: longrangeiter_methods
Eingebaut | Implementierung in C. |
---|---|
__length_hint__ | longrangeiter_len →925 |
__reduce__ | longrangeiter_reduce →931 |
__setstate__ | longrangeiter_setstate → 960 |
999〜1007: longrangeiter_dealloc(longrangeiterobject *r)
Referenzen werden in der Reihenfolge von struct member → longrangeiterobject
gelöscht.
1009〜1041: longrangeiter_next(longrangeiterobject *r)
Schließlich wird es durch Hinzufügen von Schritt * Index berechnet, um zu beginnen.
1043〜1074: PyLongRangeIter_Type
PyTypeObject |
PyRangeIter_Type |
---|---|
const char *tp_name |
"longrange_iterator" |
Py_ssize_t tp_basicsize |
sizeof(longrangeiterobject) |
destructor tp_dealloc |
(destructor)longrangeiter_dealloc →999 |
getattrofunc tp_getattro |
PyObject_GenericGetAttr |
unsigned long tp_flags |
Py_TPFLAGS_DEFAULT |
getiterfunc tp_iter |
PyObject_SelfIter |
iternextfunc tp_iternext |
(iternextfunc)longrangeiter_next →1009 |
struct PyMethodDef *tp_methods |
longrangeiter_methods →989 |
1076〜1132: range_iter(PyObject *seq)
Es gibt ein Muster, um einen Rangeiter aus rangeobject
seq zu machen, und ein Muster, um einen Longrangeiter zu machen. Die Grenze ist, ob Start, Stopp und Bereich durch den Typ C long dargestellt werden können.
1134〜1246: range_reverse(PyObject *seq)
Dies wird auch überprüft, um festzustellen, ob es in eine lange passt. Da es umgekehrt ist, muss step auch -step überprüfen. Der Rest wird durch Ersetzen von Start und Stopp gut erzeugt.
Es war eine lange Zeit, aber ich habe das Gefühl, die Atmosphäre von CPython irgendwie verstanden zu haben. Ich habe zum ersten Mal eine anständige C-Sprache gelesen und gelernt. (Ich habe gerade Shoboi C als Verfahrenssprache an der Universität gemacht)
Wie ist es also mit Python? Das Gefühl von Python ist schwierig.
Recommended Posts