Maison > développement back-end > Tutoriel Python > Exemple détaillé du problème d'indice du découpage de séquence en Python

Exemple détaillé du problème d'indice du découpage de séquence en Python

黄舟
Libérer: 2017-06-18 11:29:32
original
1107 Les gens l'ont consulté

Cet article vous présente principalement les informations pertinentes sur l'abonnement aux tranches de séquence dans Python L'article le présente en détail à travers un exemple de code, qui a une certaine valeur de référence et d'apprentissage pour tous les amis qui en ont besoin. jetez un œil ensemble ci-dessous.

Préface

En python, le découpage est une syntaxe fréquemment utilisée, qu'il s'agisse d'un tuple, d'une liste ou de String, la syntaxe générale est :

sequence[ilow:ihigh:step] # ihigh, step peut être vide ; par souci de simplicité et de facilité de compréhension, l'utilisation de step est temporairement exclue de la considération

Donnons une démonstration simple d'utilisation


sequence = [1,2,3,4,5]
sequence [ilow:ihigh] # 从ilow开始到ihigh-1结束
sequence [ilow:]  # 从ilow开始直到末尾
sequence [:ihigh]  # 从头部开始直到ihigh结束
sequence [:]   # 复制整个列表
Copier après la connexion

La syntaxe est très concise et facile à comprendre. facile à utiliser dans notre utilisation quotidienne. Mais je crois que lorsque nous utilisons cette syntaxe de découpage, nous suivrons habituellement certaines règles :

  • ilow et ihigh sont tous deux plus petits que la longueur de la séquence

  • ilow < ihigh

Parce que dans la plupart des cas, ce n'est qu'en suivant les règles ci-dessus que nous pouvons obtenir les résultats que nous attendions ! et si je ne le suis pas ? Le découpage va comment ?

Peu importe que nous utilisions des tuples, des listes ou des chaînes, lorsque nous voulons obtenir un élément, nous utiliserons la syntaxe suivante :


sequence = [1,2,3,4,5]
print sequence[1] # 输出2
print sequence[2] # 输出3
Copier après la connexion

Appelons les 1 et 2 qui apparaissent au-dessus des indices. Qu'il s'agisse d'un tuple, d'une liste ou d'une chaîne, nous pouvons utiliser l'indice pour obtenir la valeur correspondante, mais si le l'indice dépasse la longueur de l'objet, alors une exception Index (IndexError) sera déclenchée


sequence = [1,2,3,4,5]
print sequence[15] 

### 输出 ###
Traceback (most recent call last):
 File "test.py", line 2, in <module>
 print a[20]
IndexError: list index out of range
Copier après la connexion

Alors qu'en est-il du découpage ? Les deux syntaxes sont très similaires. et ihigh valent respectivement 10 et 20, alors le résultat est Que diriez-vous de ça

La scène réapparaît


# version: python2.7

a = [1, 2, 3, 5]
print a[10:20] # 结果会报异常吗?
Copier après la connexion

En voyant 10 et 20, il dépasse complètement la longueur de la séquence a. En raison du code précédent ou de l'expérience précédente, nous pensons toujours que cela provoquera certainement une IndexError. Ensuite, nous ouvrons le terminal pour le tester :


>>> a = [1, 2, 3, 5]
>>> print a[10:20]
[]
Copier après la connexion

Le résultat est en fait : [], ce qui semble un peu intéressant. Seules les listes peuvent faire cela, qu'en est-il des chaînes, qu'en est-il des tuples ?


>>> s = &#39;23123123123&#39;
>>> print s[400:2000]
&#39;&#39;
>>> t = (1, 2, 3,4)
>>> print t[200: 1000]
()
Copier après la connexion

Les résultats sont similaires à ceux de la liste, renvoyant leurs propres résultats vides.

Nous avons versé des larmes lorsque nous avons vu les résultats au lieu de renvoyer une IndexError. , nous sommes directement revenus vides. Cela nous a fait penser que la syntaxe est en fait similaire, les choses derrière elle sont définitivement différentes, alors essayons d'expliquer les résultats ensemble

Analyse des principes<🎜. >

Avant de le découvrir, nous devons d'abord comprendre comment python gère cette tranche. Nous pouvons utiliser le module dis pour nous aider :


<. 🎜>

Voici une brève introduction à dis Modules, les vétérans expérimentés savent tous que lorsque Python interprète un script, il y a aussi un processus de compilation. Le résultat de la compilation est le fichier pyc que l'on voit souvent, qui contient des octets composés de. code
############# 切片 ################
[root@iZ23pynfq19Z ~]# cat test.py
a = [11,2,3,4]
print a[20:30]

#结果:
[root@iZ23pynfq19Z ~]# python -m dis test.py 
 1   0 LOAD_CONST    0 (11)
    3 LOAD_CONST    1 (2)
    6 LOAD_CONST    2 (3)
    9 LOAD_CONST    3 (4)
    12 BUILD_LIST    4
    15 STORE_NAME    0 (a)

 2   18 LOAD_NAME    0 (a)
    21 LOAD_CONST    4 (20)
    24 LOAD_CONST    5 (30)
    27 SLICE+3    
    28 PRINT_ITEM   
    29 PRINT_NEWLINE  
    30 LOAD_CONST    6 (None)
    33 RETURN_VALUE 

############# 单下标取值 ################
[root@gitlab ~]# cat test2.py
a = [11,2,3,4]
print a[20]

#结果:
[root@gitlab ~]# python -m dis test2.py
 1   0 LOAD_CONST    0 (11)
    3 LOAD_CONST    1 (2)
    6 LOAD_CONST    2 (3)
    9 LOAD_CONST    3 (4)
    12 BUILD_LIST    4
    15 STORE_NAME    0 (a)

 2   18 LOAD_NAME    0 (a)
    21 LOAD_CONST    4 (20)
    24 BINARY_SUBSCR  
    25 PRINT_ITEM   
    26 PRINT_NEWLINE  
    27 LOAD_CONST    5 (None)
    30 RETURN_VALUE
Copier après la connexion
object object

code, et dis affiche ces bytecodes de manière plus impressionnante, nous permettant de voir le processus d'exécution. Ce qui suit est une explication des colonnes de sortie de dis : <🎜. >

La première colonne est Les chiffres sont les numéros de ligne du code source d'origine.
  • La deuxième colonne est le décalage du bytecode : LOAD_CONST est à la ligne 0. Et ainsi de suite.
  • La troisième colonne est le nom lisible par l'homme du bytecode. Ils sont préparés pour les programmeurs
  • La quatrième colonne représente les paramètres de l'instruction
  • La cinquième colonne représente les paramètres réels calculés

  • Je n'entrerai pas dans les détails ci-dessus, c'est le processus de lecture des constantes et de stockage des
  • variables
La principale différence est : test.py slice utilise le bytecode SLICE +. 3 est implémenté et la valeur d'indice unique test2.py est principalement implémentée via le bytecode BINARY_SUBSCR. Comme nous l'avons deviné, une syntaxe similaire est un code complètement différent car ce dont nous voulons discuter est le découpage (SLICE+ 3), nous ne développerons donc pas BINARY_SUBSCR. Les enfants intéressés peuvent plus vérifier le code source pertinent pour en savoir plus sur l'implémentation spécifique. Emplacement : python/object/ceval.c

Ensuite, discutons de SLICE+3

.


Bien que le code ci-dessus soit un peu long, les endroits clés ont été commentés, et nous n'avons qu'à faire attention à ces endroits. Comme ci-dessus, nous savons que nous finirons par exécuter

, mais ceci. sq_slice est un peu spécial, car différents objets ont des fonctions correspondantes différentes. Voici les fonctions correspondantes :
/*取自: python2.7 python/ceval.c */

// 第一步: 
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
  .... // 省略n行代码
  TARGET_WITH_IMPL_NOARG(SLICE, _slice)
  TARGET_WITH_IMPL_NOARG(SLICE_1, _slice)
  TARGET_WITH_IMPL_NOARG(SLICE_2, _slice)
  TARGET_WITH_IMPL_NOARG(SLICE_3, _slice)
  _slice:
  {
   if ((opcode-SLICE) & 2)
    w = POP();
   else
    w = NULL;
   if ((opcode-SLICE) & 1)
    v = POP();
   else
    v = NULL;
   u = TOP();
   x = apply_slice(u, v, w); // 取出v: ilow, w: ihigh, 然后调用apply_slice
   Py_DECREF(u);
   Py_XDECREF(v);
   Py_XDECREF(w);
   SET_TOP(x);
   if (x != NULL) DISPATCH();
   break;
  }

 .... // 省略n行代码
}

// 第二步:
apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */
{
 PyTypeObject *tp = u->ob_type;  
 PySequenceMethods *sq = tp->tp_as_sequence;

 if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) { // v,w的类型检查,要整型/长整型对象
  Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
  if (!_PyEval_SliceIndex(v, &ilow))    // 将v对象再做检查, 并将其值转换出来,存给ilow
   return NULL;
  if (!_PyEval_SliceIndex(w, &ihigh))    // 同上
   return NULL;
  return PySequence_GetSlice(u, ilow, ihigh);  // 获取u对象对应的切片函数
 }
 else {
  PyObject *slice = PySlice_New(v, w, NULL);
  if (slice != NULL) {
   PyObject *res = PyObject_GetItem(u, slice);
   Py_DECREF(slice);
   return res;
  }
  else
   return NULL;
 }

// 第三步:
PySequence_GetSlice(PyObject *s, Py_ssize_t i1, Py_ssize_t i2)
{
 PySequenceMethods *m;
 PyMappingMethods *mp;

 if (!s) return null_error();

 m = s->ob_type->tp_as_sequence;
 if (m && m->sq_slice) {
  if (i1 < 0 || i2 < 0) {
   if (m->sq_length) {
    // 先做个简单的初始化, 如果左右下表小于, 将其加上sequence长度使其归为0
    Py_ssize_t l = (*m->sq_length)(s);
    if (l < 0)
     return NULL;
    if (i1 < 0)
     i1 += l;
    if (i2 < 0)
     i2 += l;
   }
  }
  // 真正调用对象的sq_slice函数, 来执行切片的操作
  return m->sq_slice(s, i1, i2);
 } else if ((mp = s->ob_type->tp_as_mapping) && mp->mp_subscript) {
  PyObject *res;
  PyObject *slice = _PySlice_FromIndices(i1, i2);
  if (!slice)
   return NULL;
  res = mp->mp_subscript(s, slice);
  Py_DECREF(slice);
  return res;
 }

 return type_error("&#39;%.200s&#39; object is unsliceable", s);
Copier après la connexion

m->sq_slice(s, i1, i2)


Parce qu'ils sont trois, les implémentations des fonctions sont à peu près les mêmes. , nous n'avons donc besoin d'en analyser qu'un. Ce qui suit est une analyse de la fonction de découpage de la liste :

// 字符串对象
StringObject.c: (ssizessizeargfunc)string_slice, /*sq_slice*/

// 列表对象
ListObject.c: (ssizessizeargfunc)list_slice,  /* sq_slice */

// 元组
TupleObject.c: (ssizessizeargfunc)tupleslice,  /* sq_slice */
Copier après la connexion


/* 取自ListObject.c */
static PyObject *
list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
{
 PyListObject *np;
 PyObject **src, **dest;
 Py_ssize_t i, len;
 if (ilow < 0)
  ilow = 0;
 else if (ilow > Py_SIZE(a))    // 如果ilow大于a长度, 那么重新赋值为a的长度
  ilow = Py_SIZE(a);
 if (ihigh < ilow)  
  ihigh = ilow;
 else if (ihigh > Py_SIZE(a))    // 如果ihigh大于a长度, 那么重新赋值为a的长度 
  ihigh = Py_SIZE(a);
 len = ihigh - ilow;
 np = (PyListObject *) PyList_New(len); // 创建一个ihigh - ilow的新列表对象
 if (np == NULL)
  return NULL;

 src = a->ob_item + ilow;
 dest = np->ob_item;
 for (i = 0; i < len; i++) {    // 将a处于该范围内的成员, 添加到新列表对象
  PyObject *v = src[i];
  Py_INCREF(v);
  dest[i] = v;
 }
 return (PyObject *)np;
}
Copier après la connexion
Conclusion

Comme le montre la fonction de découpage correspondant à la fonction sq_slice ci-dessus, si lors de l'utilisation du découpage, les indices gauche et droit sont supérieurs à la longueur de la séquence, ils seront réaffectés à la longueur de la séquence, donc notre découpage initial : print a[10:20] , ce qui s'exécute réellement est : print a4:4 . Grâce à cette analyse, vous ne serez plus confus lorsque vous rencontrerez une tranche dont l'indice est supérieur à la longueur de l'objet~

.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal