首页 后端开发 Python教程 关于Sequence切片的下标问题及解决方法

关于Sequence切片的下标问题及解决方法

Jun 17, 2017 am 11:00 AM
下标 关于 切片 问题

这篇文章主要给大家介绍了Python中关于Sequence切片下标问题的相关资料,文中通过示例代码介绍的非常详细,对大家具有一定的参考学习价值,需要的朋友们下面来一起看看吧。

前言

在python中, 切片是一个经常会使用到的语法, 不管是元组, 列表还是字符串, 一般语法就是:

sequence[ilow:ihigh:step] # ihigh,step 可为空; 为了简短易懂, 暂时排除step的用法考虑

先来简单示范下用法


sequence = [1,2,3,4,5]
sequence [ilow:ihigh] # 从ilow开始到ihigh-1结束
sequence [ilow:]  # 从ilow开始直到末尾
sequence [:ihigh]  # 从头部开始直到ihigh结束
sequence [:]   # 复制整个列表
登录后复制

语法很简洁, 也很容易理解, 这种语法在我们日常使用中 是简单又好用, 但我相信在我们使用这种切片语法时, 都会习惯性谨遵一些规则:

  • ilow, ihigh均小于 sequece的长度

  • ilow < ihigh

因为在大部分情况下, 只有遵循上面的规则, 才能得到我们预期的结果! 可是如果我不遵循呢? 切片会怎样?

不管我们在使用元组, 列表还是字符串, 当我们想取中一个元素时, 我们会用到如下语法:


sequence = [1,2,3,4,5]
print sequence[1] # 输出2
print sequence[2] # 输出3
登录后复制

上面出现的 1,2 我们姑且称之为下标, 不管是元组, 列表还是字符串, 我们都能通过下标来取出对应的值, 但是如果下标超过对象的长度, 那么将触发索引异常(IndexError)


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
登录后复制

那么对于切片呢? 两种语法很相似, 假设我 ilow 和 ihigh分别是10和20, 那么结果是怎样呢

情景重现


# version: python2.7

a = [1, 2, 3, 5]
print a[10:20] # 结果会报异常吗?
登录后复制

看到10和20, 完全超出了序列a的长度, 由于前面的代码, 或者以前的经验, 我们总会觉得这样肯定也会导致一个IndexError,那我们开终端来试验下:


>>> a = [1, 2, 3, 5]
>>> print a[10:20]
[]
登录后复制

结果居然是: [], 这感觉有点意思.是只有列表才会这么, 字符串呢, 元组呢?


>>> s = &#39;23123123123&#39;
>>> print s[400:2000]
&#39;&#39;
>>> t = (1, 2, 3,4)
>>> print t[200: 1000]
()
登录后复制

结果都和列表的类似, 返回属于各自的空结果.

看到结果的我们眼泪掉下来, 不是返回一个IndexError, 而是直接返回空, 这让我们不禁想到, 其实语法相似, 背后的东西肯定还是不同的, 那我们下面一起来尝试去解释下这结果吧

原理分析

在揭开之前, 咱们要先搞清楚, python是怎样处理这个切片的, 可以通过dis模块来协助:


############# 切片 ################
[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
登录后复制

在这简单介绍下dis模块, 有经验的老司机都知道, python在解释脚本时, 也是存在一个编译的过程, 编译的结果就是我们经常看到的pyc文件, 这里面codeobject对象组成的字节码, 而dis就是将这些字节码用比较可观的方式展示出来, 让我们看到执行的过程, 下面是dis的输出列解释:

  • 第一列是数字是原始源代码的行号。

  • 第二列是字节码的偏移量:LOAD_CONST在第0行.以此类推。

  • 第三列是字节码人类可读的名字。它们是为程序员所准备的

  • 第四列表示指令的参数

  • 第五列是计算后的实际参数

前面就不赘述了, 就是读常量存变量的过程, 最主要的区别就是: test.py 切片是使用了字节码 SLICE+3实现的, 而test2.py 单下标取值主要通过字节码BINARY_SUBSCR实现的,如同我们猜测的一样, 相似的语法却是截然不同的代码.因为我们要展开讨论的是切片(SLICE+3), 所以就不再展开BINARY_SUBSCR, 感兴趣的童鞋可以查看相关源码了解具体实现, 位置: python/object/ceval.c

那我们下面来展开讨论下 SLICE+3


/*取自: 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);
登录后复制

虽然上面的代码有点长, 不过关键地方都已经注释出来, 而我们也只需要关注那些地方就足够了. 如上, 我们知道最终是要执行 m->sq_slice(s, i1, i2) , 但是这个sq_slice有点特别, 因为不同的对象, 它所对应的函数不同, 下面是各自的对应函数:


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

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

// 元组
TupleObject.c: (ssizessizeargfunc)tupleslice,  /* sq_slice */
登录后复制

因为他们三个的函数实现大致相同, 所以我们只分析其中一个就可以了, 下面是对列表的切片函数分析:


/* 取自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;
}
登录后复制

结论

从上面的sq_slice函数对应的切片函数可以看到, 如果在使用切片时, 左右下标都大于sequence的长度时, 都将会被重新赋值成sequence的长度, 所以咱们一开始的切片: print a[10:20] , 实际上运行的是: print a4:4 . 通过这次的分析, 以后在遇到下标大于对象长度的切片, 应该不会再懵逼了~

以上是关于Sequence切片的下标问题及解决方法的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

excel下标怎么打出来 excel下标怎么打出来 Mar 20, 2024 am 11:31 AM

e我们经常会用excel来制作一些数据表之类的,有时在输入参数数值时需要对某个数字进行上标或下标,比如数学公式就会经常用到,那么excel下标怎么打出来呢?我们一起来看看详细操作步骤:一、上标方法:1、首先Excel中输入a3(3为上标)。2、选中数字“3”,右键选择“设置单元格格式”。3、点击“上标”,然后“确定”即可。4、看,效果就是这样的。二、下标方法:1、与上标设置方法类似,在单元格中输入“ln310”(3为下标),选中数字“3”,右键选择“设置单元格格式”。2、勾选“下标”,点击“确定

聚类算法中的聚类效果评估问题 聚类算法中的聚类效果评估问题 Oct 10, 2023 pm 01:12 PM

聚类算法中的聚类效果评估问题,需要具体代码示例聚类是一种无监督学习方法,通过对数据进行聚类,将相似的样本归为一类。在聚类算法中,如何评估聚类的效果是一个重要的问题。本文将介绍几种常用的聚类效果评估指标,并给出相应的代码示例。一、聚类效果评估指标轮廓系数(SilhouetteCoefficient)轮廓系数是通过计算样本的紧密度和与其他簇的分离度来评估聚类效

python字符串切片的方法是什么 python字符串切片的方法是什么 Dec 13, 2023 pm 04:17 PM

在Python中,可以使用字符串切片来获取字符串中的子串。字符串切片的基本语法为“substring = string[start:end:step]”。

教你如何诊断常见问题的iPhone故障 教你如何诊断常见问题的iPhone故障 Dec 03, 2023 am 08:15 AM

iPhone以其强大的性能和多方面的功能而闻名,它不能幸免于偶尔的打嗝或技术困难,这是复杂电子设备的共同特征。遇到iPhone问题可能会让人感到沮丧,但通常不需要警报。在这份综合指南中,我们旨在揭开与iPhone使用相关的一些最常遇到的挑战的神秘面纱。我们的分步方法旨在帮助您解决这些常见问题,提供实用的解决方案和故障排除技巧,让您的设备恢复到最佳工作状态。无论您是面对一个小故障还是更复杂的问题,本文都可以帮助您有效地解决这些问题。一般故障排除提示在深入研究具体的故障排除步骤之前,以下是一些有助于

解决jQuery无法获取表单元素值的方法 解决jQuery无法获取表单元素值的方法 Feb 19, 2024 pm 02:01 PM

解决jQuery.val()无法使用的问题,需要具体代码示例对于前端开发者,使用jQuery是常见的操作之一。其中,使用.val()方法来获取或设置表单元素的值是非常常见的操作。然而,在一些特定的情况下,可能会出现无法使用.val()方法的问题。本文将介绍一些常见的情况以及解决方案,并提供具体的代码示例。问题描述在使用jQuery开发前端页面时,有时候会碰

如龙8酒类大师考试问题有哪些 如龙8酒类大师考试问题有哪些 Feb 02, 2024 am 10:18 AM

如龙8酒类大师考试涉及的问题包括哪些?对应的答案是什么?如何快速通过考试?酒类大师考试活动中有许多需要回答的问题,我们可以参考答案来解决。这些问题都涉及到酒的知识。如果需要参考,让我们一起来看看如龙8酒类大师考试问题答案的详细解析!如龙8酒类大师考试问题答案详解1、关于“酒”的问题。这是一种管由王室建立的蒸馏洒厂生产的蒸馏酒,以夏威夷大量种植的甘盘的糖分为原料酿制。请问这种酒叫什么?答:朗姆酒2、关于“酒”的问题。图片上是一种使用干琴洒和干苦艾酒调配而成的酒。它的特点是加入了橄榄,被誉为“鸡尼酒

使用Go语言删除切片中的元素方法介绍 使用Go语言删除切片中的元素方法介绍 Apr 02, 2024 pm 05:33 PM

Go语言中移除切片元素有三种方法:append函数(不推荐)、copy函数和手动修改底层数组。append函数可删除尾部元素,copy函数可删除中间元素,手动修改底层数组可直接赋值删除元素。

如何解决win11安装后无法使用的开始菜单问题 如何解决win11安装后无法使用的开始菜单问题 Jan 06, 2024 pm 05:14 PM

不少用户都尝试更新了win11系统,结果发现更新完后开始菜单无法使用了,这可能是因为最新的更新出现了问题,我们可以等待微软修复或者卸载这些更新来解决,下面就一起来看一下解决方法吧。win11安装后开始菜单无法使用怎么办方法一:1、首先在win11中打开控制面板。2、然后点击程序下方的“卸载程序”按钮。3、进入卸载界面,在左上角找到“查看已安装的更新”4、进入之后在更新信息中可以查看更新时间,将最近的更新全部卸载即可。方法二:1、此外,我们还可以直接下载不带更新的win11系统。2、这是一款没有最

See all articles