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 列表(List)操作方法详解
Mar 11 Python
Django中对通过测试的用户进行限制访问的方法
Jul 23 Python
Python变量赋值的秘密分享
Apr 03 Python
Python实现的求解最大公约数算法示例
May 03 Python
pandas数据筛选和csv操作的实现方法
Jul 02 Python
python原类、类的创建过程与方法详解
Jul 19 Python
flask/django 动态查询表结构相同表名不同数据的Model实现方法
Aug 29 Python
利用jupyter网页版本进行python函数查询方式
Apr 14 Python
python读取图像矩阵文件并转换为向量实例
Jun 18 Python
python3.6中anaconda安装sklearn踩坑实录
Jul 28 Python
安装pyecharts1.8.0版本后导入pyecharts模块绘图时报错: “所有图表类型将在 v1.9.0 版本开始强制使用 ChartItem 进行数据项配置 ”的解决方法
Aug 18 Python
Python趣味挑战之教你用pygame画进度条
May 31 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
一个取得文件扩展名的函数
2006/10/09 PHP
php中批量删除Mysql中相同前缀的数据表的代码
2011/07/01 PHP
第五章 php数组操作
2011/12/30 PHP
浅析php变量作用域的一些问题
2013/08/08 PHP
PHP用正则匹配form表单中所有元素的类型和属性值实例代码
2017/02/28 PHP
JS 实现完美include载入实现代码
2010/08/05 Javascript
jQuery前台数据获取实现代码
2011/03/16 Javascript
script标签属性type与language使用选择
2012/12/02 Javascript
利用js实现遮罩以及弹出可移动登录窗口
2013/07/08 Javascript
IE8的JavaScript点击事件(onclick)不兼容的解决方法
2013/11/22 Javascript
Linux下编译安装php libevent扩展实例
2015/02/14 Javascript
JavaScript DOM事件(笔记)
2015/04/08 Javascript
input file上传 图片预览功能实例代码
2016/10/25 Javascript
js构建二叉树进行数值数组的去重与优化详解
2018/03/26 Javascript
Web安全之XSS攻击与防御小结
2018/12/13 Javascript
基于JavaScript获取url参数2种方法
2020/04/17 Javascript
js实现炫酷光感效果
2020/09/05 Javascript
[56:35]DOTA2上海特级锦标赛C组小组赛#1 OG VS Archon第二局
2016/02/27 DOTA
[00:31]DOTA2上海特级锦标赛 Fnatic战队宣传片
2016/03/04 DOTA
python实现杨辉三角思路
2017/07/14 Python
代码分析Python地图坐标转换
2018/02/08 Python
Pandas读取MySQL数据到DataFrame的方法
2018/07/25 Python
django与小程序实现登录验证功能的示例代码
2019/02/19 Python
解析python的局部变量和全局变量
2019/08/15 Python
python线程安全及多进程多线程实现方法详解
2019/09/27 Python
Django 批量插入数据的实现方法
2020/01/12 Python
Django 允许局域网中的机器访问你的主机操作
2020/05/13 Python
python 删除系统中的文件(按时间,大小,扩展名)
2020/11/19 Python
python快速安装OpenCV的步骤记录
2021/02/22 Python
英国领先的鞋类零售商:Shoe Zone
2018/12/13 全球购物
创联软件面试题笔试题
2012/10/07 面试题
超市优秀员工事迹材料
2014/05/01 职场文书
被告答辩状范文
2015/05/22 职场文书
Java图书管理系统,课程设计必用(源码+文档)
2021/06/30 Java/Android
PyTorch中的torch.cat简单介绍
2022/03/17 Python
【js设计模式】SOLID五大设计原则
2022/03/24 Javascript