自定义序列的相关魔法方法允许我们自己创建的类拥有序列的特性,让其使用起来就像 python 的内置序列(dict,tuple,list,string等)。
如果要实现这个功能,就要遵循 python 的相关的协议。所谓的协议就是一些约定内容。例如,如果要将一个类要实现迭代,可以实现__iter__()
或者 __getitem__()其中一个方法
。
下面是一下相关的魔法方法:
-
定义当某一项被访问时,使用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:方式进行循环的时候。
-
迭代器要求实现next方法(python3.x中改为__next__),并且每次调用这个next方法的时候都能获得下一个元素,元素用尽时触发 StopIteration 异常。
-
而其实 for 循环的本质就是先调用对象的__iter__方法,再不断重复调用__iter__方法返回的对象的 next 方法,触发 StopIteration 异常时停止,并内部处理了这个异常,所以我们看不到异常的抛出。
这种关系就好像接口一样,如果回顾以前几篇的魔法方法,可以发现许多的内置函数得到的结果就是相应的魔法方法的返回值。
- 可迭代对象:对象实现了一个__iter__方法,这个方法负责返回一个迭代器。
- 迭代器:内部实现了next(python3.x为__next__)方法,真正负责迭代的实现。当迭代器内的元素用尽之后,任何的进一步调用都之后触发 StopIteration 异常,所以迭代器需要一个__iter__方
- 法来返回自身。所以大多数的迭代器本身就是可迭代对象。这使两者的差距进一步减少。
- 但是两者还是不同的,如果一个函数要求一个可迭代对象(iterable),而你传的迭代器(iterator)并没有实现__iter__方法,那么可能会出现错误。
- 不过一般会在一个类里同时实现这两种方法(即是可迭代对象又是迭代器),此时__iter__方法只要返回self就足够的了。当然也可以返回其它迭代器。
-
__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)
self.__index = 0
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):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 负责储