看到相似__slots__这种形如__xxx__的变量或者函数名就要注意,这些在Python中是有特殊用途的。python
__iter__app
若是一个类想被用于for ... in循环,相似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,而后,Python的for循环就会不断调用该迭代对象的next()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。函数
迭代器就是重复地作一些事情,能够简单的理解为循环,在python中实现了__iter__方法的对象是可迭代的,实现了next()方法的对象是迭代器,这样提及来有点拗口,实际上要想让一个迭代器工做,至少要实现__iter__方法和next方法。不少时候使用迭代器完成的工做使用列表也能够完成,可是若是有不少值列表就会占用太多的内存,并且使用迭代器也让咱们的程序更加通用、优雅、pythonic。spa
咱们以斐波那契数列为例,写一个Fib类,能够做用于for循环:code
class Fib(object): def __init__(self): self.a, self.b = 0, 1 # 初始化两个计数器a,b def __iter__(self): return self # 实例自己就是迭代对象,故返回本身 def next(self): self.a, self.b = self.b, self.a + self.b # 计算下一个值 if self.a > 100000: # 退出循环的条件 raise StopIteration(); return self.a # 返回下一个值
for n in Fib(): ... print n ... 1 1 2 3 5 ...
__getitem__对象
Fib实例虽然能做用于for循环,看起来和list有点像,可是,把它当成list来使用仍是不行,好比,取第5个元素:blog
Fib()[5] Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'Fib' object does not support indexing
要表现得像list那样按照下标取出元素,须要实现__getitem__()方法:继承
class Fib(object): def __getitem__(self, n): a, b = 1, 1 for x in range(n): a, b = b, a + b return a >>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3]
可是list有个神奇的切片方法:接口
>>> range(100)[5:10] [5, 6, 7, 8, 9] #对于Fib却报错。缘由是__getitem__()传入的参数多是一个int,也多是一个切片对象slice,因此要作判断: class Fib(object): def __getitem__(self, n): if isinstance(n, int): a, b = 1, 1 for x in range(n): a, b = b, a + b return a if isinstance(n, slice): start = n.start stop = n.stop a, b = 1, 1 L = [] for x in range(stop): if x >= start: L.append(a) a, b = b, a + b return L #如今试试Fib的切片: >>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] #可是没有对step参数做处理: >>> f[:10:2] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
也没有对负数做处理,因此,要正确实现一个__getitem__()仍是有不少工做要作的。内存
此外,若是把对象当作dict,__getitem__()的参数也多是一个能够做key的object,例如str。
与之对应的是__setitem__()方法,把对象视做list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。
总之,经过上面的方法,咱们本身定义的类表现得和Python自带的list、tuple、dict没什么区别,这彻底归功于动态语言的“鸭子类型”,不须要强制继承某个接口。