×
嵌入式 > 详情

Python学习要点:自定义序列实现切片功能

发布时间:2021-05-11 发布时间:
|

切片是Python中最迷人最强大最Amazing的语言特性(几乎没有之一),在《Python进阶:切片的误区与高级用法》中,我介绍了切片的基础用法、高级用法以及一些使用误区。这些内容都是基于原生的序列类型(如字符串、列表、元组……),那么,我们是否可以定义自己的序列类型并让它支持切片语法呢?更进一步,我们是否可以自定义其它对象(如字典)并让它支持切片呢?

1、魔术方法:__geTItem__()

想要使自定义对象支持切片语法并不难,只需要在定义类的时候给它实现魔术方法__geTItem__()即可。所以,这里就先介绍一下这个方法。

语法:object.__geTItem__(self,key)

官方文档释义:CalledtoimplementevaluaTIonofself[key].Forsequencetypes,theacceptedkeysshouldbeintegersandsliceobjects.Notethatthespecialinterpretationofnegativeindexes(iftheclasswishestoemulateasequencetype)isuptothe__getitem__()method.Ifkeyisofaninappropriatetype,TypeErrormayberaised;ifofavalueoutsidethesetofindexesforthesequence(afteranyspecialinterpretationofnegativevalues),IndexErrorshouldberaised.Formappingtypes,ifkeyismissing(notinthecontainer),KeyErrorshouldberaised.

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

2、自定义序列实现切片功能

接下来,我们定义一个简单的MyList,并给它加上切片功能。(PS:仅作演示,不保证其它功能的完备性)。

classMyList():def__init__(self):self.data=[]defappend(self,item):self.data.append(item)def__getitem__(self,key):print("keyis:"+str(key))returnself.data[key]l=MyList()l.append("My")l.append("name")l.append("is")l.append("Python猫")print(l[3])print(l[:2])print(l['hi'])###输出结果:keyis:3Python猫keyis:slice(None,2,None)['My','name']keyis:hiTraceback(mostrecentcalllast):...TypeError:listindicesmustbeintegersorslices,notstr

从输出结果来看,自定义的MyList既支持按索引查找,也支持切片操作,这正是我们的目的。

特别需要说明的是,此例中的__getitem__()方法会根据不同的参数类型而实现不同的功能(取索引位值或切片值),也会妥当地处理异常,所以并不需要我们再去写繁琐的处理逻辑。网上有不少学习资料完全是在误人子弟,它们会教你区分参数的不同类型,然后写一大段代码来实现索引查找和切片语法,简直是画蛇添足。下面的就是一个代表性的错误示例:

###略去其它代码####def__getitem__(self,index):cls=type(self)ifisinstance(index,slice):#如果index是个切片类型,则构造新实例returncls(self._components[index])elifisinstance(index,numbers.Integral):#如果index是个数,则直接返回returnself._components[index]else:msg="{cls.__name__}indicesmustbeintegers"raiseTypeError(msg.format(cls=cls))

3、自定义字典实现切片功能

切片是序列类型的特性,所以在上例中,我们不需要写切片的具体实现逻辑。但是,对于其它非序列类型的自定义对象,就得自己实现切片逻辑。以自定义字典为例(PS:仅作演示,不保证其它功能的完备性):

classMyDict():def__init__(self):self.data={}def__len__(self):returnlen(self.data)defappend(self,item):self.data[len(self)]=itemdef__getitem__(self,key):ifisinstance(key,int):returnself.data[key]ifisinstance(key,slice):slicedkeys=list(self.data.keys())[key]return{k:self.data[k]forkinslicedkeys}else:raiseTypeErrord=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(mostrecentcalllast):...TypeError

上例的关键点在于将字典的键值取出,并对键值的列表做切片处理,其妙处在于,不用担心索引越界和负数索引,将字典切片转换成了字典键值的切片,最终实现目的。

4、小结

本文介绍了__getitem__()魔术方法,并用于实现自定义对象(以列表类型和字典类型为例)的切片功能,希望对你有所帮助。

 


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
EDA技术在数字电路设计方案中的影响