深度解析Python切片技巧与应用

更新:10-28 民间故事 我要投稿 纠错 投诉

其实深度解析Python切片技巧与应用的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享深度解析Python切片技巧与应用的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!

小编推荐大家加入我的扣扣群735934841领取免费的Python学习资料

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:slicestepcannotbezero)

上面的一些例子对于初学者(甚至很多老手)来说可能不太容易理解,但是它们都与切片的基本语法密不可分,所以为了方便起见,我将它们包含在基本用法中。

针对这些例子,我个人总结了两条经验:

(1)牢记公式[i : i+n : m],当出现默认值时,通过想象完成公式;

(2)当索引为负,步长为正时,根据倒数计算索引位置;当索引为负且步长为负时,先翻转列表,然后根据倒数计算索引位置。

2. 切片的高级用法

一般来说,切片操作的返回结果是一个新的独立序列。以列表为例,对列表进行切片后得到的仍然是列表,占用了新的内存地址。

当切片的结果取出来时,它是一个独立的对象,因此可以用于赋值操作和其他传值场景。然而,切片只是一种浅拷贝。它复制对原始列表中元素的引用。因此,当存在变长对象的元素时,新列表将以原列表为准。

里=[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(李[8:]):

print("非空")

否则:

打印("空")

#切片列表以原列表为准

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)的拼接方法使用了占位符的思想。对于列表来说,使用切片作为占位符也可以达到拼接列表的效果。特别需要注意的是,分配给切片的值必须是可迭代对象。

里=[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:canonlyassignaniterable)

li[:0]=(9,)#[9,0,1,2,3,4,5,6,7]

li[:0]=范围(3)#[0,1,2,9,0,1,2,3,4,5,6,7]

上面的例子中,如果将切片作为独立对象取出来,会发现都是空列表,即li[:0]==li[len(li):]==li[6:6]==[],我将这种占位符称为“纯占位符”。给纯占位符赋值不会破坏原有元素,只会在特定索引位置拼接新元素。删除纯占位符时,列表中的元素也不受影响。

与“纯占位符”相对应,“非纯占位符”的切片是一个非空列表,对其进行操作(赋值和删除)会影响原始列表。如果说纯占位符可以实现列表拼接,那么不纯占位符就可以实现列表替换。

里=[1,2,3,4]

#更换不同位置

黎[: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]

#删除元素

德利[2:3]#[7,8,6,7]

切片占位符可以采取步骤来实现连续跨越替换或删除的效果。需要注意的是,该用法仅支持等长替换。

里=[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"]#错误,尝试将sequenceofsize1分配给extendedsliceofsize3

德利[:2]#[2,4,6]

3.自定义对象实现切片功能

切片是Python 中最迷人、最强大、最令人惊叹的语言功能(几乎是其中之一)。虽然上面两节介绍了切片的基本和高级用法,但这些还不足以完全展现切片的魅力。因此,在接下来的两章中,我们将重点讨论它更高级的用途。

前两节的内容是基于原生序列类型(例如字符串、列表、元组.),那么我们是否可以定义自己的序列类型并使其支持切片语法呢?更进一步,我们是否可以自定义其他对象(例如字典)并使其支持切片?

3.1.魔术方法:`getitem()`

让自定义对象支持切片语法并不困难。您只需要在定义类时为其实现魔术方法__getitem__() 即可。那么,我们先来介绍一下这个方法。

语法:object.__getitem__(self, key)

官方文件解读:呼吁实施自我评估[key]。对于序列类型,接受的键应该是整数和切片对象。请注意,负索引的特殊解释(如果类希望模拟序列类型)取决于__getitem__() 方法。如果key 的类型不合适,可能会引发TypeError ;如果值超出序列的索引集(在对负值进行任何特殊解释之后),则应引发IndexError。对于映射类型,如果密钥丢失(不在容器中),则应引发KeyError。

总结一下翻译:__getitem__()方法用于返回参数key对应的值。该键可以是整数值或切片对象,并且支持负索引;如果key不是以上两种类型,则会抛出TypeError;如果索引越界,则会抛出IndexError;如果定义了映射类型,当key参数不是其对象的key值时,会抛出KeyError。

3.2.自定义序列实现切片功能

接下来,我们定义一个简单的MyList 并为其添加切片功能。 (PS:仅供演示,不保证其他功能的完整性)。

导入号码

classMyList():

def__init__(self,anylist):

self.data=任意列表

def__len__(自身):

returnlen(self.data)

def__getitem__(自我,索引):

print("keyis:"+str(索引))

cls=类型(自身)

ifisinstance(索引,切片):

print("datais:"+str(self.data[索引]))

returncls(self.data[索引])

elifisinstance(索引,数字.Integral):

返回self.data[索引]

否则:

msg="{cls.__name__}索引必须是整数"

raiseTypeError(msg.format(cls=cls))

l=MyList(["我的","名字","是","Python猫"])

###输出结果:

钥匙is:3

蟒蛇猫

keyis:slice(无,2,无)

datais:["我的","姓名"]

__main__.MyListobjectat0x0000019CD83A7A90

钥匙:hi

回溯(最近一次调用最后一次):

.

类型Error:MyListindicesmustbeintegersorlices

从输出结果来看,定制的MyList既支持索引搜索,又支持切片操作,这正是我们的目的。

3.3.自定义字典实现切片功能

切片是序列类型的一个特性,所以在上面的例子中,我们不需要编写切片的具体实现逻辑。但是,对于其他非序列类型的自定义对象,您必须自己实现切片逻辑。以自定义词典为例(PS:仅供演示,不保证其他功能的完整性):

classMyDict():

def__init__(自身):

self.data={}

def__len__(自我):

returnlen(self.data)

defappend(自身,项目):

self.data[len(self)]=项目

def__getitem__(self,key):

ifisinstance(键,int):

返回self.data[key]

ifisinstance(键,切片):

slicedkeys=列表(self.data.keys())[键]

返回{k:self.data[k]forkinslicedkeys}

否则:

引发类型错误

d=MyDict()

d.append("我的")

d.append("名字")

d.append("是")

d.append("Python猫")

打印(d[2])

打印(d[:2])

打印(d[-4:-2])

打印(d["嗨"])

###输出结果:

{0:"我的",1:"姓名"}

{0:"我的",1:"姓名"}

回溯(最近一次调用最后一次):

.

类型错误

上面例子的关键点就是取出字典的键值,并对键值列表进行切片处理。美妙之处在于,无需担心越界索引和负索引,字典切片转换为字典键值切片。最后,实施目的。

4.Iterator实现切片功能

好了,介绍完了普通的自定义对象如何实现切片功能,这里介绍另一种不寻常的对象。

迭代器是Python中独特的高级对象。它本身不具备切片功能。但如果能用于切片,那就如同锦上添花,可以达到更加强大的效果。因此,本节将郑重介绍一下迭代器是如何实现切片功能的。

4.1.迭代和迭代器

首先需要澄清几个基本概念:迭代、可迭代对象和迭代器。

迭代是一种遍历容器类型对象(如字符串、列表、字典等)的方式。例如,当我们说迭代一个字符串“abc”时,我们的意思是从左到右将其全部取出。字符过程。 (PS:迭代这个词中文的意思是重复循环、逐层递进,但在Python中这个词应该理解为单向水平线性。如果你不熟悉,我建议你直接理解为遍历.)

那么,如何编写迭代运算的指令呢?最常见的编写语法是for循环。

#for循环实现迭代过程

forcharin"abc":

打印(字符,结束="")

#输出结果:abc

for循环可以实现迭代过程,但并不是所有的对象都可以在for循环中使用。例如,如果将上例中的字符串“abc”替换为任意整数,就会报错: "int" object is not iterable 。

这个错误中的“iterable”一词指的是“可迭代”,即int类型是不可迭代的。字符串类型是可迭代的,同样,列表、元组、字典等类型都是可迭代的。

那么如何判断一个对象是否可迭代呢?为什么它们是可迭代的?如何使对象可迭代?

要使对象可迭代,需要实现可迭代协议,即需要实现__iter__() 魔术方法。也就是说,只要实现这个魔术方法的对象是可迭代对象。

那么如何判断一个对象是否实现了这个方法呢?除了上面提到的for循环之外,我还知道另外四种方法:

#方法1:dir()视图__iter__

dir(2)#否,省略

dir("abc")#是的,稍微

#方法二:isinstance()判断

进口收藏品

isinstance(2,collections.Iterable)#False

isinstance("abc",collections.Iterable)#True

#方法三:hasattr()判断

hasattr(2,"__iter__")#False

hasattr("abc","__iter__")#True

#方法4:使用iter()检查是否报错

iter(2)#错误:"int"objectisnotiterable

迭代器("abc")#

###PS:判断是否可迭代,还可以检查__getitem__是否实现。为了描述方便,本文省略。

这些方法中最值得注意的是iter() 方法,它是Python 的内置方法,可将可迭代对象转变为迭代器。这句话可以解析为两层意思:(1)可迭代对象和迭代器是两个不同的东西; (2)可迭代对象可以成为迭代器。

事实上,迭代器一定是可迭代对象,但可迭代对象不一定是迭代器。两者的差别有多大?

如上图蓝色圆圈所示,普通可迭代对象和迭代器最关键的区别可以概括为:一和二不同,所谓“相同”,即都是可迭代的(__iter__),所谓“两异”,即可迭代对象转为迭代器后,会失去一些属性(__getitem__),同时也会获得一些属性(__next__)。

首先我们看一下新增的属性__next__。这是迭代器为何是迭代器的关键。事实上,我们定义一个同时实现__iter__ 方法和__next__ 方法的对象作为迭代器。

有了这个附加属性,可迭代对象就可以实现自己的迭代/遍历过程,而无需借助外部for 循环语法。我发明了两个概念来描述这两个遍历过程(PS:为了方便理解,这里称为遍历,但实际上也可以称为迭代):遍历是指通过外部语法实现的遍历,自遍历是指通过外部语法实现的遍历通过自己的方法。

借助这两个概念,我们说可迭代对象是可以“被它遍历”的对象,迭代器是在此基础上也可以“自遍历”的对象。

ob1="abc"

ob2=iter("abc")

ob3=iter("abc")

#ob1it 遍历

福里诺布1:

打印(i,结束="")#abc

福里诺布1:

打印(i,结束="")#abc

#ob1自遍历

ob1.__next__()#错误:"str"objecthasnoattribute"__next__"

#ob2it 遍历

福里诺布2:

打印(i,结束="")#abc

福里诺布2:

print(i,end="")#无输出

#ob2自遍历

ob2.__next__()#错误报告:StopIteration

#ob3自遍历

ob3.__next__()#a

ob3.__next__()#b

ob3.__next__()#c

ob3.__next__()#错误:停止迭代

从上面的例子可以看出,迭代器的优点就是支持自遍历。同时具有单向无环路的特点。一旦遍历完成,再次调用就会报错。

对此,我想到了一个比喻:一个普通的可迭代对象就像一个子弹匣。它通过取出子弹并在完成操作后放回去来进行遍历,因此可以重复遍历(即多次调用for循环返回相同的结果);而迭代器就像一把装满弹匣且无法拆卸的枪。当它穿越或自我穿越时,它会发射子弹。这是一个消耗性的遍历,不能重复使用(即遍历将结束)。

写了这么多,我总结一下:迭代是一种遍历元素的方式。按照实现方式来划分。有两种类型:外部迭代和内部迭代。支持外部迭代(自遍历)的对象是可迭代对象,也支持内部迭代(自遍历)的对象是迭代器;按照消耗方式可以分为可复用迭代和一次性迭代。普通的可迭代对象是可重用的,而迭代器是一次性的。

4.2.迭代器切片

前面提到“同一个东西,两个不同的东西”,最后的区别是普通的可迭代对象在转换为迭代器的过程中会丢失一些属性,其中的关键属性就是__getitem__。在上一节中,我介绍了这个神奇的方法,并用它来实现自定义对象的切片功能。

那么问题来了:为什么迭代器不继承这个属性呢?

首先,迭代器采用消费式遍历,这意味着它充满了不确定性,即它的长度和索引键值对是动态衰减的,因此很难获取到它的item,不再需要__getitem_ 。 _属性。其次,强行给迭代器添加这个属性也是不合理的。俗话说,强行努力并不甜……

由此,一个新的问题出现了:既然如此重要的属性(包括其他未识别的属性)会丢失,为什么还要使用迭代器呢?

这个问题的答案是,迭代器具有不可替代、强大且有用的功能,这就是Python这样设计迭代器的原因。由于篇幅限制,这里我就不展开展开了。这个话题我以后会专门补一下。

还没完,一个挥之不去的问题来了:即使迭代器继续支持切片,迭代器还能有这个属性吗?

hi="欢迎关注公众号:Python猫"

它=iter(嗨)

#普通切片

你好[-7:]#Pythoncat

#Counterexample:迭代器切片

it[-7:]#Error: "str_iterator"objectisnotsubscriptable

迭代器不能使用普通的切片语法,因为它们缺少__getitem__。如果要实现切片,有两种思路:一是自己造轮子,写实现逻辑;二是自己造轮子,写实现逻辑。另一个是寻找封装轮子。

Python的itertools模块就是我们要找的轮子,它提供了轻松实现迭代器切片的方法。

导入工具

#示例1:简单迭代器

s=iter("123456789")

forxinitertools.islice(s,2,6):

print(x,end="")#输出:3456

forxinitertools.islice(s,2,6):

print(x,end="")#输出:9

#示例2:斐波那契数列迭代器

类Fib():

def__init__(自身):

自身.a,自身.b=1,1

def__iter__(自我):

whileTrue:

yieldself.a

self.a,self.b=self.b,self.a+self.b

f=iter(Fib())

forxinitertools.islice(f,2,6):

print(x,end="")#输出:2358

forxinitertools.islice(f,2,6):

print(x,end="")#输出:345589144

itertools模块的islice()方法将迭代器与切片完美结合,最终回答了前面的问题。然而,迭代器切片与普通切片相比有很多局限性。首先,这个方法不是“纯函数”(纯函数需要遵守“相同的输入得到相同的输出”的原则);其次,它只支持正向切片,不支持负索引。这是由于迭代器的有损性质造成的。决定了。

那么,我不禁要问:itertools模块的切片方法是采用什么实现逻辑的呢?下面是官网提供的源码:

defislice(可迭代,*args):

#islice("ABCDEFG",2)--AB

#islice("ABCDEFG",2,4)--CD

#islice("ABCDEFG",2,无)--CDEFG

#islice("ABCDEFG",0,无,2)--ACEG

s=切片(*args)

#索引区间为[0,sys.maxsize],默认步长为1

开始、停止、步长=s.startor0、s.stoporsys.maxsize、s.stepor1

it=iter(范围(开始、停止、步长))

尝试:

下一个=下一个(它)

exceptStopIteration:

#消耗*iter

able* up to the *start* position. fori, elementinzip(range(start), iterable): pass return try: fori, elementinenumerate(iterable): ifi == nexti: yieldelement nexti = next(it) exceptStopIteration: # Consume to *stop*. fori, elementinzip(range(i +1, stop), iterable): pass islice() 方法的索引方向是受限的,但它也提供了一种可能性:即允许你对一个无穷的(在系统支持范围内)迭代器进行切片的能力。这是迭代器切片最具想象力的用途场景。 除此之外,迭代器切片还有一个很实在的应用场景:读取文件对象中给定行数范围的数据。 我们知道,从文件中读取内容主要有两种方法(参见之前关于文件读写的文章):read() 适合读取内容较少的情况,或者是需要一次性处理全部内容的情况;而 readlines() 适用性更广,因为它是迭代地读取内容,既减少内存压力,又方便逐行对数据处理。 虽然 readlines() 有迭代读取的优势,但它是从头到尾逐行读取,若文件有几千行,而我们只想要读取少数特定行(例如第1000-1009行),那它还是效率太低了。考虑到文件对象天然就是迭代器 ,我们可以使用迭代器切片先行截取,然后再处理,如此效率将大大地提升。 # test.txt 文件内容 """ 猫 Python猫 python is a cat. this is the end. """ fromitertoolsimportislice withopen("test.txt","r",encoding="utf-8")asf: print(hasattr(f,"__next__"))# 判断是否迭代器 content = islice(f,2,4) forlineincontent: print(line.strip()) ### 输出结果: True pythonisa cat. thisisthe end. 本节内容较多,简单回顾一下:迭代器是一种特殊的可迭代对象,可用于它遍历与自遍历,但遍历过程是损耗型的,不具备循环复用性,因此,迭代器本身不支持切片操作;通过借助 itertools 模块,我们能实现迭代器切片,将两者的优势相结合,其主要用途在于截取大型迭代器(如无限数列、超大文件等等)的片段,实现精准的处理,从而大大地提升性能与效率。 5、小结 最后总结一下,切片是 Python 的一种高级特性,常用于截取序列类型的元素,但并不局限于此,本文主要介绍了它的基础用法、高级用法(如占位符用法)、自定义对象切片、以及迭代器切片等使用内容。除此之外,切片还有更广阔多样的使用场景,例如 Numpy 的多维切片、内存视图切片、异步迭代器切片等等,都值得我们去探索一番。

深度解析Python切片技巧与应用和的问题分享结束啦,以上的文章解决了您的问题吗?欢迎您下次再来哦!

用户评论

念旧是个瘾。

想要更好地理解Python里面的切片操作吗?这篇博文刚好来帮你!

    有19位网友表示赞同!

怅惘

学习Python切片的技巧总是很有用的,能提高代码效率。

    有20位网友表示赞同!

oО清风挽发oО

我之前在做数据处理的时候遇到过切片的问题,希望这篇文章能帮我解答一些疑惑。

    有9位网友表示赞同!

ヅ她的身影若隐若现

终于找到一篇详细讲解Python切片的文章了!

    有19位网友表示赞同!

逃避

感觉切片是Python中最基础的操作之一,但细细品味やっぱり很多技巧!

    有7位网友表示赞同!

将妓就计

学习编程真的需要不断积累这些小知识点,这篇文章挺实用的。

    有14位网友表示赞同!

不识爱人心

最近在用Python做项目,遇到了一些关于切片的用法不确定的地方,这篇分析刚好可以帮到我!

    有13位网友表示赞同!

丢了爱情i

想提高Python代码的精炼度,掌握好切片技巧真是必须的!

    有19位网友表示赞同!

あ浅浅の嘚僾

看完这篇文章,对Python切片的理解又深入了一层。

    有10位网友表示赞同!

仅有的余温

分享给正在学习Python的朋友们,这篇博文讲解很清楚易懂!

    有9位网友表示赞同!

三年约

以前觉得Python切片简单,但看了这篇分析才发现真正厉害的地方在于细节!

    有18位网友表示赞同!

逾期不候

做数据挖掘的时候经常用到切片操作,这篇文章内容很有用!

    有13位网友表示赞同!

風景綫つ

学习Python一直需要不断练习和学习新方法,这篇文章很棒!

    有12位网友表示赞同!

艺菲

想用Python写出高效的代码,这篇博文是一份很好的指导!

    有15位网友表示赞同!

米兰

Python切片的代码示例讲解得很贴近实际项目场景。

    有6位网友表示赞同!

羁绊你

很实用的一篇关于Python切片用法分析文章,值得收藏!

    有16位网友表示赞同!

゛指尖的阳光丶

这篇文章让我对Python切片的运用有了更强的理解力!

    有13位网友表示赞同!

不相忘

对于学习Python的新手来说,这篇博客讲解非常有帮助!

    有15位网友表示赞同!

千城暮雪

文章内容清晰易懂,能够帮助我快速掌握Python切片的使用方法!

    有8位网友表示赞同!

【深度解析Python切片技巧与应用】相关文章:

1.动物故事精选:寓教于乐的儿童故事宝库

2.《寓教于乐:精选动物故事助力儿童成长》

3.探索动物旅行的奇幻冒险:专为儿童打造的童话故事

4.《趣味动物刷牙小故事》

5.探索坚韧之旅:小蜗牛的勇敢冒险

6.传统风味烤小猪,美食探索之旅

7.探索奇幻故事:大熊的精彩篇章

8.狮子与猫咪的奇妙邂逅:一场跨界的友谊故事

9.揭秘情感的力量:如何影响我们的生活与决策

10.跨越两岸:探索彼此的独特世界

上一篇:《犬夜叉》1080p中文字幕完整版高清动漫在线观看资源汇总 下一篇:芈姝与芈月关系逐渐疏远:历史人物的必然轨迹