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 相关文章推荐
一则python3的简单爬虫代码
May 26 Python
Python中的ceil()方法使用教程
May 14 Python
Django中使用locals()函数的技巧
Jul 16 Python
Python通过正则表达式选取callback的方法
Jul 18 Python
Python中文竖排显示的方法
Jul 28 Python
python对配置文件.ini进行增删改查操作的方法示例
Jul 28 Python
详解如何利用Cython为Python代码加速
Jan 27 Python
Python发送邮件测试报告操作实例详解
Dec 08 Python
python fuzzywuzzy模块模糊字符串匹配详细用法
Aug 29 Python
Python批量将图片灰度化的实现代码
Apr 11 Python
python可视化 matplotlib画图使用colorbar工具自定义颜色
Dec 07 Python
将不规则的Python多维数组拉平到一维的方法实现
Jan 11 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
基于HBase Thrift接口的一些使用问题及相关注意事项的详解
2013/06/03 PHP
php把大写命名转换成下划线分割命名
2015/04/27 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
ThinkPHP3.2框架自定义配置和加载用法示例
2018/06/14 PHP
PHP随机生成中文段落示例【测试网站内容时使用】
2020/04/26 PHP
Javascript 调试利器 Firebug使用详解六
2009/07/05 Javascript
原生Js实现按的数据源均分时间点幻灯片效果(已封装)
2010/12/28 Javascript
ToolTips JQEURY插件之简洁小提示框效果
2011/11/19 Javascript
setInterval与clearInterval的使用示例代码
2014/01/28 Javascript
js加减乘除丢失精度问题解决方法
2014/05/16 Javascript
Javascript表单验证要注意的事项
2014/09/29 Javascript
jquery简单实现带渐显效果的选项卡菜单代码
2015/09/01 Javascript
JS实现选中当前菜单后高亮显示的导航条效果
2015/10/15 Javascript
js实现点击每个li节点,都弹出其文本值及修改
2016/12/15 Javascript
jQuery与js实现颜色渐变的方法
2016/12/30 Javascript
详解Vue Elementui中的Tag与页面其它元素相互交互的两三事
2018/09/25 Javascript
玩转Koa之koa-router原理解析
2018/12/29 Javascript
微信小程序定义和调用全局变量globalData的实现
2019/11/01 Javascript
Jquery $.map使用方法实例详解
2020/09/01 jQuery
js获取url页面id,也就是最后的数字文件名
2020/09/25 Javascript
[43:24]2018DOTA2亚洲邀请赛3月29日 小组赛A组 LGD VS Liquid
2018/03/30 DOTA
[44:37]完美世界DOTA2联赛PWL S3 Forest vs access 第一场 12.11
2020/12/13 DOTA
python select.select模块通信全过程解析
2017/09/20 Python
基于Python os模块常用命令介绍
2017/11/03 Python
Python类的继承、多态及获取对象信息操作详解
2019/02/28 Python
基于python+selenium自动健康打卡的实现代码
2021/01/13 Python
HTML5触摸事件(touchstart、touchmove和touchend)的实现
2020/05/08 HTML / CSS
意大利在线高尔夫商店:Online Golf
2021/03/09 全球购物
C#如何判断当前用户是否输入某个域
2015/12/07 面试题
视光学专业毕业生推荐信
2013/10/28 职场文书
汽车运用工程专业求职信
2014/06/18 职场文书
2015年学校财务工作总结
2015/05/19 职场文书
2016年领导干部正风肃纪心得体会
2015/10/09 职场文书
《用字母表示数》教学反思
2016/02/17 职场文书
python字符串的一些常见实用操作
2022/04/06 Python
Nginx开源可视化配置工具NginxConfig使用教程
2022/06/21 Servers