python魔法方法-自定义序列详解


Posted in Python onJuly 21, 2016

自定义序列的相关魔法方法允许我们自己创建的类拥有序列的特性,让其使用起来就像 python 的内置序列(dict,tuple,list,string等)。

如果要实现这个功能,就要遵循 python 的相关的协议。所谓的协议就是一些约定内容。例如,如果要将一个类要实现迭代,就必须实现两个魔法方法:__iter__、next(python3.x中为__new__)。__iter__应该返回一个对象,这个对象必须实现 next 方法,通常返回的是 self 本身。而 next 方法必须在每次调用的时候都返回下一个元素,并且当元素用尽时触发 StopIteration 异常。

而其实 for 循环的本质就是先调用对象的__iter__方法,再不断重复调用__iter__方法返回的对象的 next 方法,触发 StopIteration 异常时停止,并内部处理了这个异常,所以我们看不到异常的抛出。

这种关系就好像接口一样,如果回顾以前几篇的魔法方法,可以发现许多的内置函数得到的结果就是相应的魔法方法的返回值。

下面是一下相关的魔法方法:

•__len__(self)

•返回容器的长度。可变和不可变容器都要实现它,这是协议的一部分。

•__getitem__(self, key)

•定义当某一项被访问时,使用self[key]所产生的行为。这也是可变容器和不可变容器协议的一部分。如果键的类型错误将产生TypeError;如果key没有合适的值则产生KeyError。

•__setitem__(self, key, value)

•定义当一个条目被赋值时,使用self[key] = value所产生的行为。这也是可变容器协议的一部分。而且,在相应的情形下也会产生KeyError和TypeError。

•__delitem__(self, key)

•定义当某一项被删除时所产生的行为。(例如del self[key])。这是可变容器协议的一部分。当你使用一个无效的键时必须抛出适当的异常。

•__iter__(self)

•返回一个容器迭代器,很多情况下会返回迭代器,尤其是当内置的iter()方法被调用的时候,以及当使用for x in container:方式循环的时候。迭代器是它们本身的对象,它们必须定义返回self的__iter__方法。

•__reversed__(self)

•实现当reversed()被调用时的行为。应该返回序列反转后的版本。仅当序列是有序的时候实现它,例如列表或者元组。

•__contains__(self, item)

•定义了调用in和not in来测试成员是否存在的时候所产生的行为。这个不是协议要求的内容,但是你可以根据自己的要求实现它。当__contains__没有被定义的时候,Python会迭代这个序列,并且当找到需要的值时会返回True。

•__missing__(self, key)

•其在dict的子类中被使用。它定义了当一个不存在字典中的键被访问时所产生的行为。(例如,如果我有一个字典d,当"george"不是字典中的key时,使用了d["george"],此时d.__missing__("george")将会被调用)。

下面是一个代码示例:

class Foo(object):
  def __init__(self, key, value):
    self.key = []
    self.value = []
    self.key.append(key)
    self.value.append(value)

  def __len__(self):
    return len(self.key)

  def __getitem__(self, item):
    try:
      __index = self.key.index(item)
      return self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __setitem__(self, key, value):
    if key not in self.key:
      self.key.append(key)
      self.value.append(value)
    else:
      __index = self.key.index(key)
      self.value[__index] = value

  def __delitem__(self, key):
    try:
      __index = self.key.index(key)
      del self.key[__index]
      del self.value[__index]
    except ValueError:
      raise KeyError('can not find the key')

  def __str__(self):
    result_list = []
    for index in xrange(len(self.key)):
      __key = self.key[index]
      __value = self.value[index]
      result = __key, __value
      result_list.append(result)
    return str(result_list)

  def __iter__(self):
    self.__index = 0
    return self

  def next(self):
    if self.__index == len(self.key):
      self.__index = 0
      raise StopIteration()
    else:
      __key = self.key[self.__index]
      __value = self.value[self.__index]
      result = __key, __value
      self.__index += 1
      return result

  def __reversed__(self):
    __result = self.value[:]
    __result.reverse()
    return __result

  def __contains__(self, item):
    if item in self.value:
      return True
    else:
      return False

这里创建一个模拟字典的类,这个类的内部维护了两个列表,key 负责储存键,value 负责储存值,两个列表通过索引的一一对应,从而达到模拟字典的目的。

首先,我们看看__len__方法,按照协议,这个方法应该返回容器的长度,因为这个类在设计的时候要求两个列表必须等长,所以理论上返回哪个列表的长度都是一样的,这里我选择返回 key 的长度。

然后是__getitem__方法。这个方法会在a['scolia']时,调用a.__getitem__('scolia')。也就是说这个方法定义了元素的获取,我这里的思路是先找到 key 列表中建的索引,然后用索引去 value 列表中找对应的元素,然后将其返回。然后为了进一步伪装成字典,我捕获了可能产生的 ValueError (这是 item 不在 key 列表中时触发的异常),并将其伪装成字典找不到键时的 KeyError。

理论上只要实现了上面两个方法,就可以得到一个不可变的容器了。但是我觉得并不满意所以继续拓展。

__setitem__(self, key, value)方法定义了 a['scolia'] = 'good' 这种操作时的行为,此时将会调用a.__setitem__('scolia', 'good') 因为是绑定方法,所以self是自动传递的,我们不用理。这里我也模拟了字典中对同一个键赋值时会造成覆盖的特性。这个方法不用返回任何值,所以return语句也省略了。

__delitem__(self, key)方法定义了del a['scolia'] 这类操作时候的行为,里面的‘scolia'就作为参数传进去。这里也进行了异常的转换。

只有实现里以上四个方法,就可以当做可变容器来使用了。有同学可能发现并没有切片对应的魔法方法,而事实上,我也暂时没有找到先,这部分内容先搁着一边。

接下来的 __str__ 是对应于 str() 函数,在类的表示中会继续讨论,这里是为了 print 语句好看才加进去的,因为print语句默认就是调用str()函数。

__iter__和next方法在开头的时候讨论过了,这里是为了能让其进行迭代操作而加入的。

__reversed__(self)方法返回一个倒序后的副本,这里体现了有序性,当然是否需要还是要看个人。

__contains__实现了成员判断,这里我们更关心value列表中的数据,所以判断的是value列表。该方法要求返回布尔值。

下面是相应的测试:

a = Foo('scolia', 'good')
a[123] = 321
a[456] = 654
a[789] = 987
print a
del a[789]
print a
for x, y in a:
  print x, y
print reversed(a)
print 123 in a
print 321 in a

python魔法方法-自定义序列详解

•__missing__(self, key)

class Boo(dict):
  def __new__(cls, *args, **kwargs):
    return super(Boo, cls).__new__(cls)

  def __missing__(self, key):
    return 'The key(%s) can not be find.'% key

测试:

b = Boo()
b['scolia'] = 'good'
print b['scolia']
print b['123']

 python魔法方法-自定义序列详解

当然你也可以在找不到 key 的时候触发异常,具体实现看个人需求。

以上这篇python魔法方法-自定义序列详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python GAE、Django导出Excel的方法
Nov 24 Python
在 Django/Flask 开发服务器上使用 HTTPS
Jul 03 Python
Python中用psycopg2模块操作PostgreSQL方法
Nov 28 Python
python3.6.3转化为win-exe文件发布的方法
Oct 31 Python
pyinstaller打包多个py文件和去除cmd黑框的方法
Jun 21 Python
pandas dataframe的合并实现(append, merge, concat)
Jun 24 Python
Python实现对adb命令封装
Mar 06 Python
python中使用you-get库批量在线下载bilibili视频的教程
Mar 10 Python
python图片指定区域替换img.paste函数的使用
Apr 09 Python
python安装sklearn模块的方法详解
Nov 28 Python
python 递归相关知识总结
Mar 03 Python
对pytorch中x = x.view(x.size(0), -1) 的理解说明
Mar 03 Python
浅谈Python 字符串格式化输出(format/printf)
Jul 21 #Python
分享python数据统计的一些小技巧
Jul 21 #Python
python中print的不换行即时输出的快速解决方法
Jul 20 #Python
Python全局变量用法实例分析
Jul 19 #Python
python对象及面向对象技术详解
Jul 19 #Python
python异常和文件处理机制详解
Jul 19 #Python
python线程、进程和协程详解
Jul 19 #Python
You might like
php数据入库前清理 注意php intval与mysql的int取值范围不同
2010/12/12 PHP
php中jQuery插件autocomplate的简单使用笔记
2012/06/14 PHP
CodeIgniter生成网站sitemap地图的方法
2013/11/13 PHP
php中heredoc与nowdoc介绍
2014/12/25 PHP
Laravel-admin之修改操作日志的方法
2019/09/30 PHP
日期函数扩展类Ver0.1.1
2006/09/07 Javascript
JavaScript 原型链学习总结
2010/10/29 Javascript
基于jquery自己写tab滑动门(通用版)
2012/10/30 Javascript
jquery中one()方法的用法实例
2015/01/16 Javascript
深入理解JavaScript 函数
2016/06/06 Javascript
jQuery 3.0 的 setter和getter 模式详解
2016/07/11 Javascript
微信小程序 视图层(xx.xml)和逻辑层(xx.js)详细介绍
2016/10/13 Javascript
jQuery插件HighCharts绘制2D圆环图效果示例【附demo源码下载】
2017/03/09 Javascript
javascript 正则表达式分组、断言详解
2017/04/20 Javascript
jQuery Tree Multiselect使用详解
2017/05/02 jQuery
JavaScript 中定义函数用 var foo = function () {} 和 function foo()区别介绍
2018/03/01 Javascript
微信小程序利用canvas 绘制幸运大转盘功能
2018/07/06 Javascript
vue 配置多页面应用的示例代码
2018/10/22 Javascript
three.js 利用uv和ThreeBSP制作一个快递柜功能
2020/08/18 Javascript
python中zip和unzip数据的方法
2015/05/27 Python
django批量导入xml数据
2016/10/16 Python
python中判断文件编码的chardet(实例讲解)
2017/12/21 Python
python实现移位加密和解密
2019/03/22 Python
python求一个字符串的所有排列的实现方法
2020/02/04 Python
Python grpc超时机制代码示例
2020/09/14 Python
Python实现淘宝秒杀功能的示例代码
2021/01/19 Python
前端canvas动画如何转成mp4视频的方法
2019/06/17 HTML / CSS
使用placeholder属性设置input文本框的提示信息
2020/02/19 HTML / CSS
电影T恤、80年代T恤和80年代服装:TV Store Online
2020/01/05 全球购物
Solaris操作系统的线程机制
2012/12/23 面试题
自荐信格式范文
2013/10/07 职场文书
医学生自荐信
2013/12/03 职场文书
化学实验员岗位职责
2013/12/28 职场文书
大学生收银员求职信分享
2014/01/02 职场文书
五好文明家庭事迹材料
2014/12/20 职场文书
保卫工作个人总结
2015/03/03 职场文书