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 正则表达式入门(初级篇)
Dec 07 Python
Python sqlite3事务处理方法实例分析
Jun 19 Python
Python 装饰器使用详解
Jul 29 Python
Windows下Anaconda的安装和简单使用方法
Jan 04 Python
python通过ffmgep从视频中抽帧的方法
Dec 05 Python
django项目环境搭建及在虚拟机本地创建django项目的教程
Aug 02 Python
python3.7实现云之讯、聚合短信平台的短信发送功能
Sep 26 Python
使用IDLE的Python shell窗口实例详解
Nov 19 Python
jupyter notebook 重装教程
Apr 16 Python
Keras 实现加载预训练模型并冻结网络的层
Jun 15 Python
Django如何实现密码错误报错提醒
Sep 04 Python
Python帮你解决手机qq微信内存占用太多问题
Feb 15 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
Yii2框架中一些折磨人的坑
2019/12/15 PHP
JS类的封装及实现代码
2009/12/02 Javascript
javascript 仿QQ滑动菜单效果代码
2010/09/03 Javascript
关于URL中的特殊符号使用介绍
2011/11/03 Javascript
Js日期选择自动填充到输入框(界面漂亮兼容火狐)
2013/08/02 Javascript
jQuery点击自身以外地方关闭弹出层的简单实例
2013/12/24 Javascript
jquery实现简单实用的弹出层效果代码
2015/10/15 Javascript
谈一谈javascript闭包
2016/01/28 Javascript
javascript+HTML5自定义元素播放焦点图动画
2016/02/21 Javascript
JavaScript动态添加事件之事件委托
2016/07/12 Javascript
AngularJS入门教程之表格实例详解
2016/07/27 Javascript
nodejs使用express创建一个简单web应用
2017/03/31 NodeJs
值得收藏的vuejs安装教程
2017/11/21 Javascript
用Axios Element实现全局的请求loading的方法
2018/03/15 Javascript
angular6的table组件开发的实现示例
2018/12/26 Javascript
vscode下vue项目中eslint的使用方法
2019/01/13 Javascript
基于vue-cli 路由 实现类似tab切换效果(vue 2.0)
2019/05/08 Javascript
jQuery Migrate 插件用法实例详解
2019/05/22 jQuery
Vue + Element UI图片上传控件使用详解
2019/08/20 Javascript
解决layui的input独占一行的问题
2019/09/10 Javascript
python实现向ppt文件里插入新幻灯片页面的方法
2015/04/28 Python
python将字符串转换成json的方法小结
2019/07/09 Python
django之对FileField字段的upload_to的设定方法
2019/07/28 Python
使用pyecharts生成Echarts网页的实例
2019/08/12 Python
tensorflow2.0的函数签名与图结构(推荐)
2020/04/28 Python
python如何更新包
2020/06/11 Python
Python字符串函数strip()原理及用法详解
2020/07/23 Python
TOWER London官网:鞋子、靴子、运动鞋等
2019/07/14 全球购物
Erwin Müller穆勒家居瑞士官网:您整个家庭的邮购公司
2019/12/28 全球购物
西安众合通用.net笔试题
2013/03/18 面试题
给同事的道歉信
2014/01/11 职场文书
高三毕业典礼主持词
2014/03/27 职场文书
北京天坛导游词
2015/02/12 职场文书
单位同意报考证明
2015/06/17 职场文书
浅谈为什么我的 z-index 又不生效了
2022/07/15 HTML / CSS
PostgreSQL逻辑复制解密原理解析
2022/09/23 PostgreSQL