Python进阶之自定义对象实现切片功能


Posted in Python onJanuary 07, 2019

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

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 。

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

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

class MyList():
 def __init__(self):
  self.data = []
 def append(self, item):
  self.data.append(item)
 def __getitem__(self, key):
  print("key is : " + str(key))
  return self.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'])

### 输出结果:
key is : 3
Python猫
key is : slice(None, 2, None)
['My', 'name']
key is : hi
Traceback (most recent call last):
...
TypeError: list indices must be integers or slices, not str

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

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

###略去其它代码####
def __getitem__(self, index):
 cls = type(self)
 if isinstance(index, slice): # 如果index是个切片类型,则构造新实例
  return cls(self._components[index])
 elif isinstance(index, numbers.Integral): # 如果index是个数,则直接返回
  return self._components[index]
 else:
  msg = "{cls.__name__} indices must be integers"
  raise TypeError(msg.format(cls=cls))

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、小结

最后小结一下:本文介绍了__getitem__() 魔术方法,并用于实现自定义对象(以列表类型和字典类型为例)的切片功能,希望对你有所帮助。也希望大家多多支持三水点靠木。

Python 相关文章推荐
利用Python批量提取Win10锁屏壁纸实战教程
Mar 27 Python
Python产生Gnuplot绘图数据的方法
Nov 09 Python
对python制作自己的数据集实例讲解
Dec 12 Python
python简单实现AES加密和解密
Mar 28 Python
Python3.5面向对象编程图文与实例详解
Apr 24 Python
python小程序实现刷票功能详解
Jul 17 Python
python使用html2text库实现从HTML转markdown的方法详解
Feb 21 Python
Python文件读写w+和r+区别解析
Mar 26 Python
python对指定字符串逆序的6种方法(小结)
Apr 02 Python
Python使用configparser读取ini配置文件
May 25 Python
解决Tkinter中button按钮未按却主动执行command函数的问题
May 23 Python
利用Matlab绘制各类特殊图形的实例代码
Jul 16 Python
Python基于matplotlib画箱体图检验异常值操作示例【附xls数据文件下载】
Jan 07 #Python
python读取几个G的csv文件方法
Jan 07 #Python
实时获取Python的print输出流方法
Jan 07 #Python
Python 运行 shell 获取输出结果的实例
Jan 07 #Python
在python 中实现运行多条shell命令
Jan 07 #Python
Python之使用adb shell命令启动应用的方法详解
Jan 07 #Python
python 对多个csv文件分别进行处理的方法
Jan 07 #Python
You might like
给php新手谈谈我的学习心得
2007/02/25 PHP
php zlib压缩和解压缩swf文件的代码
2008/12/30 PHP
php实现网站插件机制的方法
2009/11/10 PHP
PHP安装memcached扩展笔记
2015/05/28 PHP
java微信开发之上传下载多媒体文件
2016/06/24 PHP
PHP 接入微信扫码支付总结(总结篇)
2016/11/03 PHP
JS 面向对象之神奇的prototype
2011/02/26 Javascript
Javascript简单实现可拖动的div
2013/10/22 Javascript
JS获取月的最后一天与JS得到一个月份最大天数的实例代码
2013/12/16 Javascript
jquery基础教程之deferred对象使用方法
2014/01/22 Javascript
jQuery 如何先创建、再修改、后添加DOM元素
2014/05/20 Javascript
JavaScript显示当前文档最后修改日期的方法
2015/03/19 Javascript
Jquery代码实现图片轮播效果(一)
2015/08/12 Javascript
JQuery ztree带筛选、异步加载实例讲解
2016/02/25 Javascript
真正好用的js验证上传文件大小的简单方法
2016/10/27 Javascript
Jquery+Ajax+xml实现中国地区选择三级联动菜单效果(推荐)
2017/06/09 jQuery
JS解决IOS中拍照图片预览旋转90度BUG的问题
2017/09/13 Javascript
React Native时间转换格式工具类分享
2017/10/24 Javascript
实例解析Vue.js下载方式及基本概念
2018/05/11 Javascript
浅析JS中回调函数及用法
2018/07/25 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
2018/08/08 Javascript
Vue 使用formData方式向后台发送数据的实现
2019/04/14 Javascript
vue学习笔记之过滤器的基本使用方法实例分析
2020/02/01 Javascript
Element MessageBox弹框的具体使用
2020/07/27 Javascript
用pywin32实现windows模拟鼠标及键盘动作
2014/04/22 Python
python调用百度地图WEB服务API获取地点对应坐标值
2019/01/16 Python
pymysql 开启调试模式的实现
2019/09/24 Python
英国手工布艺沙发在线购买:Sofas & Stuff
2018/03/02 全球购物
巴西女装购物网站:Eclectic
2018/04/24 全球购物
经济管理自荐书
2014/06/09 职场文书
安徽导游词
2015/02/12 职场文书
高温慰问简报
2015/07/21 职场文书
2017寒假社会实践心得体会范文
2016/01/14 职场文书
2019事业单位个人工作总结范文
2019/08/26 职场文书
您对思维方式了解多少?
2019/12/09 职场文书
React Native项目框架搭建的一些心得体会
2021/05/28 Javascript