Comme je m'inquiétais de "Python-ness" depuis un an, je lisais l'implémentation de range
.
Utilisons xrange
au lieu de range
! C'était un super déraillement par rapport à l'histoire de l'ère Python 2.
Dépôt: https://hg.python.org/cpython Miroir: https://github.com/python/cpython (Je ne suis pas sûr de Mercurial, donc tous les suivants sont Git)
https://github.com/python/cpython/tree/master/Objects Il y a diverses choses intéressantes dans la ligne. Tous sont longs et illisibles.
Source files for various builtin objects
Il semble qu'il y ait eu une histoire selon laquelle «range» et «xrange» sont différents en Python2. Il semble que Python 3 n'a pas à s'en soucier. Il semble qu'il y ait quelque chose qui s'appelle 2to3, et il y a aussi un programme qui convertit correctement «xrange» en «range». https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_xrange.py
Cette fois https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Objects/rangeobject.c Je lis. Il y a 1246 lignes, mais le niveau de difficulté est probablement faible.
3〜4: include
Le fichier d'en-tête est https://github.com/python/cpython/tree/master/Include C'est dedans.
Que lire dans rangeobject.c
typedef struct PyMemberDef
Deux de.
Le tableau de PyMemberDef
semble définir les noms, les types et les décalages des membres de la structure C.
rangeobject
6〜19: struct rangeobject
L'objet de la gamme de structure
avoir. Cela ressemble à une gamme.
PyObject_HEAD
et PyObject
sont décrits dans Structures d'objets communs et tous les types d'objets Est une extension de PyObject
.
La valeur PY_SSIZE_T_MAX apparaît dans le commentaire. Py_ssize_t
est un entier signé contrairement à ssize_t
. ssize_t
est mentionné dans PEP 353 --Utilisation de ssize_t comme type d'index | Python.org.
21〜39: validate_step(PyObject *step)
Fonction auxiliaire pour l'étape de vérification. L'étape 0 ne convient pas comme plage, il semble donc qu'il la vérifie. Si aucune étape n'est spécifiée, elle est considérée comme 1.
41〜42: compute_range_length(PyObject *start, PyObject *stop, PyObject *step)
→ Ligne 151
44〜64: make_range_object(PyTypeObject *type, PyObject *start, PyObject *stop, PyObject *step)
Créez rangeobject
à partir de l'argument. C'est le range_new suivant qui crée vraiment rangeobject
, et s'il n'y a pas de problème après avoir vérifié diverses choses avec range_new, make_range_object est appelé.
Puisque rangeobject
est un objet, c'est une extension de PyObject
. Donc, après avoir créé PyObject
, donnez la valeur de range (start etc.).
Qu'est-ce que PyObject_New (type, typeobj)
? (Type *) _PyObject_New (typeobj)
[indique](https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Include/objim.# -L135), qui crée PyObject
en allouant de la mémoire. Pour plus d'informations, suivez object.c. Abréviation.
66〜129: range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
Effectuez correctement diverses vérifications et créez rangeobject
. Si une valeur inappropriée est transmise, renoncez à la créer, allouez la mémoire allouée et renvoyez NULL
.
Il y avait un commentaire comme celui-ci.
/* 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
Les documents pour la plage (arrêt) et la plage (début, arrêt [, étape]) sont écrits à l'aide d'une macro appelée PyDoc_STRVAR. PyDoc_STRVAR
#define PyDoc_VAR(name) static char name[]
Arriver à. C'est une macro pour les documents en ligne.
141〜149: range_dealloc(rangeobject *r)
Libération des membres de struct rangeobject
→ Libération de la structure.
151〜227: compute_range_length(PyObject *start, PyObject *stop, PyObject *step)
Calcule et renvoie le nombre d'éléments dans la plage (lo, hi, step). L'algorithme semble être le même que get_len_of_range (lo, hi, step) à partir de la ligne 865, et l'histoire détaillée y est écrite. La différence est que l'argument est «PyObject» et que «PyObject» est considéré comme «PyLong» et l'histoire continue.
229〜233: range_length(rangeobject *r)
Renvoie la longueur du membre sous la forme Py_ssize_t
.
Le PyLong_AsSsize_t utilisé ici semble renvoyer Py_ssize_t
à partir d'un objet de type int.
235〜248: compute_item(rangeobject *r, PyObject *i)
Calculez la valeur à partir de start, i et step et obtenez la valeur suivante. Le commentaire dit aussi return r-> start + (i * r-> step)
. Tel quel.
250〜307: compute_range_item(rangeobject *r, PyObject *arg)
Ceux qui appellent le compute_item ci-dessus.
Avant cela, i de compute_item (rangeobject * r, PyObject * i) est émis à partir de arg (en fait, il semble être PyLong
) et la plage est vérifiée.
309〜319: range_item(rangeobject *r, Py_ssize_t i)
Le côté qui appelle l'élément compute_range_item ci-dessus. Cela prend en charge les arguments et les références de fonction.
321〜358: compute_slice(rangeobject *r, PyObject *_slice)
Faites une tranche de «rangeobject». Il gère également les échecs et libère les références.
360〜409: range_contains_long(rangeobject *r, PyObject *ob)
Dans le prochain range_contains, range_index, range_count
PyLong_CheckExact(ob) || PyBool_Check(ob)
Est exécuté avec la coche. PyLong_CheckExact et [PyBool_Check](https://github.com/python/c8bython/fr /boolobject.h#L12) est également une macro qui effectue une vérification de type avec la même signification.
Quant au contenu, ob est-il simplement dans la plage indiquée par l'objet range (= existe-t-il)?
411〜419: range_contains(rangeobject *r, PyObject *ob)
(PyLong_CheckExact(ob) || PyBool_Check(ob))
Puis la gamme ci-dessus_contains_Utiliser longtemps, sinon_PySequence_IterSearchFaites un jugement avec.
421〜465: range_equals(rangeobject *r0, rangeobject *r1)
Une fonction qui compare deux «rangeobject». Un commentaire indique où chercher pour prendre une décision.
/*
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)
L'argument est PyObject
, mais il y a un check pour voir si autre est PyRange_Type
.
op pointe vers l'opérateur de comparaison (https://github.com/python/cpython/blob/28b093620c3ae9be02449ea0cecd8b4d649c80d2/Include/object.h#L927), et cette fonction cible Py_NE
et Py_EQ
seulement.
Appelez finalement range_equals.
497〜550: range_hash(rangeobject *r)
Fonction de hachage pour rangeobject
. Renvoyer avec le type Py_hash_t. Les valeurs utilisées pour obtenir le hachage sont len (), start et step. Le traitement est modifié en fonction de la valeur de len (r).
/* 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)
Au moment de la portée_contains_long, sinon_PySequence_Comptez avec IterSearch.
572〜602: range_index(rangeobject *r, PyObject *ob)
PyLong_CheckExact(ob) || PyBool_Check(ob)
Au moment de la portée_contains_long, sinon_PySequence_Recherchez avec IterSearch.
Pour range_contains_long, après vérification de l'existence
idx = PyNumber_FloorDivide(tmp, r->step);
L'emplacement est calculé et diffusé.
604〜613: range_as_sequence
Enregistrez diverses fonctions selon PySequenceMethods. ici
PySequenceMethods |
range_as_sequence |
---|---|
lenfunc sq_length |
range_longueur → ligne 229 |
ssizeargfunc sq_item |
range_élément → ligne 309 |
objobjproc sq_contains |
range_contient → ligne 411 |
Seuls sont enregistrés.
615〜633: range_repr(rangeobject *r)
Sortez la chaîne de sortie dans PyUnicode. Lorsque l'étape est 1, l'étape est omise.
635〜641: range_reduce(rangeobject *r, PyObject *args)
Cela semble être une fonction auxiliaire de pickle. Py_BuildValue ([\ _Py_BuildValue_SizeTc](\ _Py_BuildValue_SizeTc Donnez les informations de struct rangeobject
à Python / modsupport.c # L441-L450)).
643〜662: range_subscript(rangeobject* self, PyObject* item)
l'élément est Si vous passez par PyIndex_Check (en bref, int), vous pouvez utiliser compute_range_item. Si vous passez par PySlice_Check (essentiellement slice), vous pouvez utiliser compute_slice. Renvoie l'élément. Sinon, c'est une erreur.
665〜669: range_as_mapping
Cette fois, il est enregistré selon PyMappingMethods. 2 fonctions sur 3 sont implémentées.
PyMappingMethods |
range_as_mapping |
---|---|
lenfunc mp_length |
range_longueur → ligne 229 |
binaryfunc mp_subscript |
range_indice → ligne 643 |
671: range_iter(PyObject *seq)
→ Ligne 1076
672: range_reverse
→ Ligne 1134
674〜682: PyDoc
Documents à inverser, compter et indexer.
684〜690: range_methods
PyMethodDef relie les fonctions sur Python avec les fonctions implémentées en C, les informations sur les arguments et la documentation. chose.
Ici, les fonctions suivantes sont liées.
Intégré | Implémentation en C |
---|---|
__reversed__ | range_reverse →1134 |
__reduce__ | range_reduce →635 |
count | range_count →552 |
index | range_index →572 |
692〜697: range_members
PyMemberDef représente le nom, le type et le décalage des membres de la structure C. Ici, il s'agit d'un tableau d'informations de démarrage, d'arrêt et d'étape.
699〜738: PyRange_Type
Une définition de type appelée PyRange_Type
. Il existe divers éléments de paramétrage, mais dans le cas de la plage, les informations suivantes sont enregistrées Il y a.
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 |
À propos, PyVarObject_HEAD_INIT semble être lié à la définition du segment initial de PyObject
.
rangeiterobject
740〜753: struct rangeiterobject
La définition est similaire à «rangeobject», mais légèrement différente.
755〜764: rangeiter_next(rangeiterobject *r)
Obtenez la valeur suivante en ajoutant une étape d'index * pour commencer. Pour éviter tout débordement, il est converti en non signé et renvoyé.
766〜770: rangeiter_len(rangeiterobject *r)
Calculez la longueur avec len --index.
772〜773: PyDoc
length_hint_doc. Cela semble être une méthode privée qui donne une estimation de la longueur de la liste.
775〜802: rangeiter_reduce(rangeiterobject *r)
Fonction pour cornichon. Cela prend plus de temps que l'objet range car il y a une conversion en PyLong
.
804〜817: rangeiter_setstate(rangeiterobject *r, PyObject *state)
Définissez la valeur dans l'index.
819〜820: PyDoc
doc pour réduire et définir l'état. Les deux semblent être liés au cornichon.
822〜830: rangeiter_methods
Intégré | Implémentation en 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 |
Je me demande pourquoi il y a PyObject_Del et PyObject_DEL ...
865〜891: get_len_of_range(long lo, long hi, long step)
Renvoie le nombre d'éléments dans la plage (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.
---------------------------------------------------------------*/
La description du commentaire est plus longue que le code. Le point est simplement de diviser la section par le nombre d'étapes.
893〜915: fast_range_iter(long start, long stop, long step)
Fabriquez un télémètre. S'il est plus long que le type long de C, NULL est renvoyé en tant qu'erreur.
longrangeiterobject
917〜923: struct longrangeiterobject
C'est «PyObject».
925〜929: longrangeiter_len(longrangeiterobject *r, PyObject *no_args)
Utilisez PyNumber_Subtract pour soustraire l'index de len.
931〜958: longrangeiter_reduce(longrangeiterobject *r)
Pour cornichon. Calculez l'arrêt et calculez la valeur en créant un objet range avec make_range_object.
960〜987: longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
Mettez l'état dans l'index.
989〜997: longrangeiter_methods
Intégré | Implémentation en C |
---|---|
__length_hint__ | longrangeiter_len →925 |
__reduce__ | longrangeiter_reduce →931 |
__setstate__ | longrangeiter_setstate → 960 |
999〜1007: longrangeiter_dealloc(longrangeiterobject *r)
Les références sont supprimées dans l'ordre struct member → longrangeiterobject
.
1009〜1041: longrangeiter_next(longrangeiterobject *r)
Après tout, il est calculé en ajoutant step * index pour commencer.
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)
Il existe un modèle pour créer un télémètre à partir de la séquence rangeobject
et un motif pour créer un longrangeiter. La limite est de savoir si le début, l'arrêt et la plage peuvent être représentés par le type long C.
1134〜1246: range_reverse(PyObject *seq)
Ceci est également vérifié pour voir si cela tient dans un long. Puisqu'il est inversé, step doit également vérifier -step. Le reste est généré en remplaçant bien démarrer et arrêter.
Cela faisait longtemps, mais j'ai l'impression d'avoir en quelque sorte compris l'atmosphère de CPython. J'ai lu un langage C décent pour la première fois et je l'ai appris. (Je viens de faire Shoboi C comme langage procédural à l'université)
Alors, après tout, à quoi ressemble Python? Le sentiment de Python est difficile.
Recommended Posts