[PYTHON] J'ai lu l'implémentation de range (Objects / rangeobject.c)

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.

À propos de CPython

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

À propos de la gamme

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.

Essayons

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

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

génération iter

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.

Impressions

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

J'ai lu l'implémentation de range (Objects / rangeobject.c)
Lire l'implémentation de la minuterie globale ARM
J'ai lu et implémenté les variantes de UKR
J'ai suivi la mise en place de la commande du (première moitié)
J'ai suivi la mise en place de la commande du (seconde moitié)
J'ai lu l'article de SHAP
J'ai étudié le mécanisme de connexion flask!
Je veux lire la version html de la version "OpenCV-Python Tutorials" OpenCV 3.1
[Python] J'ai expliqué en détail la théorie et la mise en œuvre de la régression logistique
[Python] J'ai expliqué en détail la théorie et la mise en œuvre de l'arbre de décision
J'ai fait réfléchir AI aux paroles de Genshi Yonezu (implémentation)
J'ai essayé de résumer la méthode de mise en œuvre fréquemment utilisée de pytest-mock
J'ai vérifié les options de copyMakeBorder d'OpenCV
Othello-De la troisième ligne de "Implementation Deep Learning" (3)
La structure des dossiers de Flask est résumée
Je ne connaissais pas les bases de Python
Je vois, je vais lire le processus UNIX
Le modèle de projet Python auquel je pense.
Lire tout le contenu de proc / [pid]
Othello-De la troisième ligne de "Implementation Deep Learning" (2)
[Python] J'ai expliqué en détail la théorie et l'implémentation de la machine à vecteurs de support (SVM).
[Python] Lire le code source de Bottle Part 2
J'ai essayé l'analyse par grappes de la carte météo
J'ai résolu le problème le plus profond d'Hiroshi Yuki.
Pourquoi l'implémentation Python d'ISUCON 5 a utilisé Bottle
Organiser la signification des méthodes, des classes et des objets
Spécification de la plage des tableaux ruby et python
J'ai vérifié la liste des touches de raccourci de Jupyter
J'ai essayé de corriger la forme trapézoïdale de l'image
Lire la sortie du sous-processus, ouvrir en temps réel
[Python] Lire le code source de Bottle Part 1
Essayez Progate Free Edition [Python I]
J'ai vérifié la période de rétention de session de django
J'ai vérifié la vitesse de traitement de la numpy unidimensionnelle
J'ai touché certaines des nouvelles fonctionnalités de Python 3.8 ①
Je souhaite personnaliser l'apparence de zabbix
J'ai essayé d'utiliser le filtre d'image d'OpenCV
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé de trier les objets de l'image du plat de steak-④ Clustering
J'ai jeté un œil au contenu de sklearn (scikit-learn) (1) ~ Qu'en est-il de l'implémentation de CountVectorizer? ~
[Recette du formateur] J'ai touché le flacon du framework Python.
Modèle de script python pour lire le contenu du fichier
Je veux grep le résultat de l'exécution de strace
Jouez avec l'implémentation de l'interface utilisateur de Pythonista 3 [Super Super Primer]
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé le tutoriel MNIST de tensorflow pour les débutants.
The End of Catastrophic Programming # 04 "Mistaken Exception Catch Range"
J'ai comparé l'identité des images par moment Hu
Peut-être ai-je surestimé l'impact de Shell Shock sur CGI
Visualisez la gamme d'insertions internes et externes avec python
Implémentation python de la classe de régression linéaire bayésienne
À propos des tests dans la mise en œuvre de modèles d'apprentissage automatique
J'ai vérifié les spécifications de sortie du LSTM bidirectionnel de PyTorch
J'ai vérifié les versions de Blender et Python
Je veux bien comprendre les bases de Bokeh
Les performances de PHP étaient meilleures que ce à quoi je m'attendais