Python全面解读高级特性切片
作者:豌豆花下猫 发布时间:2021-06-05 14:23:13
目录
1、切片的基础用法
2、切片的高级用法
3、自定义对象实现切片功能
3.1、魔术方法:`getitem()`
3.2、自定义序列实现切片功能
3.3、自定义字典实现切片功能
4、迭代器实现切片功能
4.1、迭代与迭代器
4.2、迭代器切片
5、小结
前言:
众所周知,我们可以通过索引值(或称下标)来查找序列类型(如字符串、列表、元组…)中的单个元素,那么,如果要获取一个索引区间的元素该怎么办呢?
切片(slice
)就是一种截取索引片段的技术,借助切片技术,我们可以十分灵活地处理序列类型的对象。通常来说,切片的作用就是截取序列对象,然而,对于非序列对象,我们是否有办法做到切片操作呢?在使用切片的过程中,有什么要点值得重视,又有什么底层原理值得关注呢?本文将主要跟大家一起来探讨这些内容,希望我能与你共同学习进步。
1、切片的基础用法
列表是 Python 中极为基础且重要的一种数据结构,也是最能发挥切片的用处的一种数据结构,所以在前两节,我将以列表为例介绍切片的一些常见用法。
首先是切片的书写形式:[i : i+n : m] ;其中,i 是切片的起始索引值,为列表首位时可省略;i+n 是切片的结束位置,为列表末位时可省略;m 可以不提供,默认值是1,不允许为0 ,当m为负数时,列表翻转。注意:这些值都可以大于列表长度,不会报越界。
切片的基本含义是:从序列的第i位索引起,向右取到后n位元素为止,按m间隔过滤 。
li = [1, 4, 5, 6, 7, 9, 11, 14, 16]
# 以下写法都可以表示整个列表,其中 X >= len(li)
li[0:X] == li[0:] == li[:X] == li[:]
== li[::] == li[-X:X] == li[-X:]
li[1:5] == [4,5,6,7] # 从1起,取5-1位元素
li[1:5:2] == [4,6] # 从1起,取5-1位元素,按2间隔过滤
li[-1:] == [16] # 取倒数第一个元素
li[-4:-2] == [9, 11] # 从倒数第四起,取-2-(-4)=2位元素
li[:-2] == li[-len(li):-2]
== [1,4,5,6,7,9,11] # 从头开始,取-2-(-len(li))=7位元素
# 步长为负数时,列表先翻转,再截取
li[::-1] == [16,14,11,9,7,6,5,4,1] # 翻转整个列表
li[::-2] == [16,11,7,5,1] # 翻转整个列表,再按2间隔过滤
li[:-5:-1] == [16,14,11,9] # 翻转整个列表,取-5-(-len(li))=4位元素
li[:-5:-3] == [16,9] # 翻转整个列表,取-5-(-len(li))=4位元素,再按3间隔过滤
# 切片的步长不可以为0
li[::0] # 报错(ValueError: slice step cannot be zero)
上述的某些例子对于初学者(甚至很多老手)来说,可能还不好理解,但是它们都离不开切片的基本语法,所以为方便起见,我将它们也归入基础用法中。
对于这些样例,我个人总结出两条经验:
(1)牢牢记住公式[i : i+n : m],当出现缺省值时,通过想象把公式补全;
(2)索引为负且步长为正时,按倒数计算索引位置;索引为负且步长为负时,先翻转列表,再按倒数计算索引位置。
2、切片的高级用法
一般而言,切片操作的返回结果是一个新的独立的序列,以列表为例,列表切片后得到的还是一个列表,占用新的内存地址。
当取出切片的结果时,它是一个独立对象,因此,可以将其用于赋值操作,也可以用于其它传递值的场景。但是,切片只是浅拷贝 ,它拷贝的是原列表中元素的引用,所以,当存在变长对象的元素时,新列表将受制于原列表。
li = [1, 2, 3, 4]
ls = li[::]
li == ls # True
id(li) == id(ls) # False
li.append(li[2:4]) # [1, 2, 3, 4, [3, 4]]
ls.extend(ls[2:4]) # [1, 2, 3, 4, 3, 4]
# 下例等价于判断li长度是否大于8
if(li[8:]):
print("not empty")
else:
print("empty")
# 切片列表受制于原列表
lo = [1,[1,1],2,3]
lp = lo[:2] # [1, [1, 1]]
lo[1].append(1) # [1, [1, 1, 1], 2, 3]
lp # [1, [1, 1, 1]]
由于可见,将切片结果取出,它可以作为独立对象使用,但是也要注意,是否取出了变长对象的元素。
切片既可以作为独立对象被“取出”原序列,也可以留在原序列,作为一种占位符使用。
不久前,我介绍了几种拼接字符串的方法(链接见文末),其中三种格式化类的拼接方法(即 %、format()
、template
)就是使用了占位符的思想。对于列表来说,使用切片作为占位符,同样能够实现拼接列表的效果。特别需要注意的是,给切片赋值的必须是可迭代对象。
li = [1, 2, 3, 4]
# 在头部拼接
li[:0] = [0] # [0, 1, 2, 3, 4]
# 在末尾拼接
li[len(li):] = [5,7] # [0, 1, 2, 3, 4, 5, 7]
# 在中部拼接
li[6:6] = [6] # [0, 1, 2, 3, 4, 5, 6, 7]
# 给切片赋值的必须是可迭代对象
li[-1:-1] = 6 # (报错,TypeError: can only assign an iterable)
li[:0] = (9,) # [9, 0, 1, 2, 3, 4, 5, 6, 7]
li[:0] = range(3) # [0, 1, 2, 9, 0, 1, 2, 3, 4, 5, 6, 7]
上述例子中,若将切片作为独立对象取出,那你会发现它们都是空列表,即 li[:0]==li[len(li):]==li[6:6]==[] ,我将这种占位符称为“纯占位符”,对纯占位符赋值,并不会破坏原有的元素,只会在特定的索引位置中拼接进新的元素。删除纯占位符时,也不会影响列表中的元素。
与“纯占位符”相对应,“非纯占位符”的切片是非空列表,对它进行操作(赋值与删除),将会影响原始列表。如果说纯占位符可以实现列表的拼接,那么,非纯占位符可以实现列表的替换。
li = [1, 2, 3, 4]
# 不同位置的替换
li[:3] = [7,8,9] # [7, 8, 9, 4]
li[3:] = [5,6,7] # [7, 8, 9, 5, 6, 7]
li[2:4] = ['a','b'] # [7, 8, 'a', 'b', 6, 7]
# 非等长替换
li[2:4] = [1,2,3,4] # [7, 8, 1, 2, 3, 4, 6, 7]
li[2:6] = ['a'] # [7, 8, 'a', 6, 7]
# 删除元素
del li[2:3] # [7, 8, 6, 7]
切片占位符可以带步长,从而实现连续跨越性的替换或删除效果。需要注意的是,这种用法只支持等长替换。
li = [1, 2, 3, 4, 5, 6]
li[::2] = ['a','b','c'] # ['a', 2, 'b', 4, 'c', 6]
li[::2] = [0]*3 # [0, 2, 0, 4, 0, 6]
li[::2] = ['w'] # 报错,attempt to assign sequence of size 1 to extended slice of size 3
del li[::2] # [2, 4, 6]
3、自定义对象实现切片功能
切片是 Python
中最迷人最强大最 Amazing
的语言特性(几乎没有之一),以上两小节虽然介绍了切片的基础用法与高级用法,但这些还不足以充分地展露切片的魅力,所以,在接下来的两章节中,我们将聚焦于它的更高级用法。
前两节内容都是基于原生的序列类型(如字符串、列表、元组……),那么,我们是否可以定义自己的序列类型并让它支持切片语法呢?更进一步,我们是否可以自定义其它对象(如字典)并让它支持切片呢?
3.1、魔术方法:`getitem()`
想要使自定义对象支持切片语法并不难,只需要在定义类的时候给它实现魔术方法 __getitem__()
即可。所以,这里就先介绍一下这个方法。
语法:object.__getitem__(self, key)
官方文档释义:Called to implement evaluation of self[key]. For sequence types, the accepted keys should be integers and slice objects. Note that the special interpretation of negative indexes (if the class wishes to emulate a sequence type) is up to the __getitem__() method. If key is of an inappropriate type, TypeError may be raised; if of a value outside the set of indexes for the sequence (after any special interpretation of negative values), IndexError should be raised. For mapping types, if key is missing (not in the container), KeyError should be raised.
概括翻译一下:__getitem__() 方法用于返回参数 key 所对应的值,这个 key 可以是整型数值和切片对象,并且支持负数索引;如果 key 不是以上两种类型,就会抛 TypeError;如果索引越界,会抛 IndexError ;如果定义的是映射类型,当 key 参数不是其对象的键值时,则会抛 KeyError 。
3.2、自定义序列实现切片功能
接下来,我们定义一个简单的 MyList ,并给它加上切片功能。(PS:仅作演示,不保证其它功能的完备性)。
import numbers
class MyList():
def __init__(self, anylist):
self.data = anylist
def __len__(self):
return len(self.data)
def __getitem__(self, index):
print("key is : " + str(index))
cls = type(self)
if isinstance(index, slice):
print("data is : " + str(self.data[index]))
return cls(self.data[index])
elif isinstance(index, numbers.Integral):
return self.data[index]
else:
msg = "{cls.__name__} indices must be integers"
raise TypeError(msg.format(cls=cls))
l = MyList(["My", "name", "is", "Python猫"])
### 输出结果:
key is : 3
Python猫
key is : slice(None, 2, None)
data is : ['My', 'name']
<__main__.MyList object at 0x0000019CD83A7A90>
key is : hi
Traceback (most recent call last):
...
TypeError: MyList indices must be integers or slices
从输出结果来看,自定义的 MyList
既支持按索引查找,也支持切片操作,这正是我们的目的。
3.3、自定义字典实现切片功能
切片是序列类型的特性,所以在上例中,我们不需要写切片的具体实现逻辑。但是,对于其它非序列类型的自定义对象,就得自己实现切片逻辑。以自定义字典为例(PS:仅作演示,不保证其它功能的完备性):
class MyDict():
def __init__(self):
self.data = {}
def __len__(self):
return len(self.data)
def append(self, item):
self.data[len(self)] = item
def __getitem__(self, key):
if isinstance(key, int):
return self.data[key]
if isinstance(key, slice):
slicedkeys = list(self.data.keys())[key]
return {k: self.data[k] for k in slicedkeys}
else:
raise TypeError
d = MyDict()
d.append("My")
d.append("name")
d.append("is")
d.append("Python猫")
print(d[2])
print(d[:2])
print(d[-4:-2])
print(d['hi'])
### 输出结果:
is
{0: 'My', 1: 'name'}
{0: 'My', 1: 'name'}
Traceback (most recent call last):
...
TypeError
上例的关键点在于将字典的键值取出,并对键值的列表做切片处理,其妙处在于,不用担心索引越界和负数索引,将字典切片转换成了字典键值的切片,最终实现目的。
4、迭代器实现切片功能
来源:https://mp.weixin.qq.com/s?__biz=MzUyOTk2MTcwNg==&mid=2247483894&idx=1&sn=0f354bfa77d3647137f0cdda97c64a0b&scene=21#wechat_redirect


猜你喜欢
- 如下所示:import numpy as npx = [1,2] #横坐标y = [3,4] #第一个纵坐标y1 =
- 1、从记录中选出所有fault_code列的值在fault_list= [487, 479, 500, 505]这个范围内的记录r
- scrapy.FormRequestlogin.pyclass LoginSpider(scrapy.Spider): name =
- 有时候我们不希望浏览器使用缓存加快网页的显示,尤其是那些论坛之类的频繁更新内容的网页,在网上有说可以使用下面的方法来屏蔽缓存,但是我试了效果
- tcp.py # -*- coding: cp936 -*-import socketfrom struct import *from ti
- Python最长回文子串1.暴力解法(Brute Method)暴力求解是最容易想到的,要截取字符串的所有子串,然后再判断这些子串中哪些是回
- v-model指令 所谓的“指令”其实就是扩展了HTML标签功能(属性)。先来一个组件,不用vue-model,正常父子通信<!--
- 本文实例为大家分享了python实现局域网内聊天功能的具体代码,供大家参考,具体内容如下功能: 可以向局域网内开启接收信息功能的ip进行发送
- 目录写在前面双向加密ENCODE/DECODEAES_ENCRYPT/AES_DECRYPTDES_ENCRYPT/DES_DECRYPT单
- 前言:大家都知道,只要进行数据交互,肯定就要去请求接口,数据请求的方式有vue-resource axios fetch
- 本文实例讲述了python网络编程之读取网站根目录的方法,分享给大家供大家参考。具体实现方法如下:import socket, sys po
- 上传控件基础知识说明:上传控件(<input type="file"/>)用于在客户端浏览并上传文件,用户选
- 本文介绍基于Python中ArcPy模块,对大量不同时相的栅格遥感影像按照其成像时间依次执行批量拼接的方法。在前期的文章Python arc
- 概述迭代器是访问集合元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。延迟计算或惰性
- Python——re模块 简介定义:re模块称为正则表达式;作用:创建一个"规则表达式",用于验证和查找符合规
- mutilprocess像线程一样管理进程,这个是mutilprocess的核心,他与threading很是相像,对多核CPU的
- 任务1、我的咖啡馆你做主元组coffee_name=('蓝山','卡布奇诺','拿铁',
- 1.首先分析要做的项目的结构,整理出关系图2.运行 python manage.py startapp XXX 创建一个app3.根据关系图
- PyTorch基础入门二:PyTorch搭建一维线性回归模型1)一维线性回归模型的理论基础给定数据集,线性回归希望能够优化出一个好的函数,使
- 前言Vscode是是一个强大的跨平台工具,我自己电脑是mac,公司电脑是win而且是内部环境,导致公司安装软件很费劲。好在vscode许多插