2018-12-31更新声明:切片系列文章原为三篇,现合并为一篇。合并后修复了一些严重的错误(比如自定义序列切片的部分),在文本结构和章节连接上做了很多改动。原系列的单篇文章就不删了,毕竟也是作为单独的文章。在此声明,请阅读改进版——Python进阶:高级特性切片全面解读!https://mp.weixin.qq.com/s/IR...切片是Python中最迷人、最强大、最神奇的语言特性(几乎没有),在《Python进阶:切片的误区与高级用法》中,我介绍了基本用法、高级用法和一些误解。这些内容都是基于原生的序列类型(比如字符串、列表、元组……),那么,我们能不能定义自己的序列类型,让它支持切片语法呢?更进一步,我们能不能自定义其他对象(比如字典),让它支持切片呢?1.魔术方法:__getitem__()让自定义对象支持切片语法并不难,只需要在定义类的时候为其实现魔术方法__getitem__()即可。所以,这里首先介绍一下这个方法。语法:object.__getitem__(self,key)官方文档解读:调用实现对self[key]的求值。对于序列类型,接受的键应该是整数和切片对象。请注意,负索引的特殊解释(如果类希望模拟序列类型)取决于__getitem__()方法。如果键的类型不合适,可能会引发TypeError;如果值超出序列的索引集(在对负值进行任何特殊解释之后),则应引发IndexError。对于映射类型,如果缺少键(不在容器中),则应引发KeyError。通用翻译:__getitem__()方法用于返回参数key对应的值,可以是整型Value和slice对象,支持负索引;如果key不是以上两种类型,会抛出TypeError;如果索引越界,将抛出IndexError;如果定义的是映射类型,当key参数不是其对象的key值时,就会抛出KeyError。2.自定义sequence实现切片功能接下来,我们定义一个简单的MyList,并为其添加切片功能。(PS:仅作演示,不保证其他功能的完整性)。类MyList():def__init__(self):自我。data=[]defappend(self,item):self.数据。append(item)def__getitem__(self,key):print("keyis:"+str(key))returnself.data[key]l=MyList()l.append("我的")l.append("函数name")l.append("is")l.append("Python猫")print(l[3])print(l[:2])print(l['hi'])###输出结果:键是:3Pythoncat键是:slice(None,2,None)['My','name']键是:hiTraceback(最近一次调用last):...TypeError:列表索引必须是整数或切片,而不是str从输出结果来看,自定义的MyList同时支持索引查找和切片操作,这就是我们的目的。特别是本例中的__getitem__()方法会根据不同的参数类型实现不同的功能(取索引位值或分片值),同时也会妥善处理异常,所以我们不需要再去写繁琐的处理逻辑。网上有很多学习资料,完全是误导。他们会教你区分不同类型的参数,然后写一大段代码实现索引搜索和切片语法,简直画蛇添足。下面是一个有代表性的错误示例:###Omitothercode####def__getitem__(self,index):cls=type(self)ifisinstance(index,slice):#如果index是slice类型,则构造anewinstancereturncls(self._components[index])elifisinstance(index,numbers.Integral):#如果index是数字,直接returnreturnself._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):返回自我。data[key]ifisinstance(key,slice):slicedkeys=list(self.data.keys())[key]返回{k:self.data[k]forkinslicedkeys}else:raiseTypeErrord=MyDict()d.追加(“我的”)d。append("name")d.append("is")d.append("PythonCat")print(d[2])print(d[:2])print(d[-4:-2])print(d['hi'])###outputresult:is{0:'My',1:'name'}{0:'My',1:'name'}Traceback(mostrecentcalllast):.上面例子中..TypeError的关键点是取出字典的键值,对键值列表进行分片。妙处在于将字典切片转换为字典键值的切片,不用担心索引越界和负索引,最终达到目的。4、小结最后小结:本文介绍了__getitem__()魔术方法,用于实现自定义对象的切片功能(以列表类型和字典类型为例)。希望对您有所帮助。参考阅读:Python进阶:切片的误区和高级用法官方文档getitem用法:http://t.cn/EbzoZypPython切片赋值源码分析:http://t.cn/EbzSaoZPS:本公众号(Python猫)已开通读者交流群。详情请到菜单栏“交流群”了解更多。---------------本文首发于微信公众号【蟒猫】,后台回复“爱学习”,送20+精选e-免费书籍。
