目录:
-
iterable对象
-
iterator对象, 数据类型Iterator类
-
数据类型Generator类。
-
生成器表达式
-
collections.abc:容器的抽象基类。用于判断具体类。
-
itertools模块:很多生成iterator的函数。
-
延伸:duck-typing:理解python动态语言;再看继承和多态
iterable -- 可迭代对象
能够逐一返回其成员项的对象。包括:
- 有序类型list, str, tuple
- 无序类型dict, set
- 任何定义了__iter__, __next__的类的对象。
可迭代对象被可用于 for
循环以及许多其他需要一个序列的地方(zip()
、map()
...)。
当一个可迭代对象作为参数传给内置函数 iter()
时,它会返回该对象的迭代器。这种iterator适用于对值集合的一次性遍历。
例子:
>>> a = [1, 2, None] >>> a [1, 2, None] >>> iter(a) <list_iterator object at 0x106a28f10>
for语句会自动为可迭代对象创建一个的迭代器。用于循环期间的操作。
iterator -- 迭代器
用来表示一连串数据流的对象。重复调用迭代器的 __next__()
方法(或将其传给内置函数 next()
)将逐个返回流中的项。
大多数容器对象都可以使用for循环语句:
这幕后的机制就是,for循环内部使用了iter(), 为容器对象生成一个iterator对象。这个对象使用__next__()来逐一输出容器对象内的数据。
数据类型--Iterator类型
即对容器对象提供迭代的支持。
container.__iter__()
返回一个iterator对象。它有2个方法:
- iterator.__iter__(): 返回自身,用于配合for和in语句。
- iterator.__next__(): 从容器中返回下一项item。
Python定义了几种iterator对象,用于对序列类型list,str,tuple,字典dict, 和其他特别的形式进行迭代操作。
数据类型--generator类型
generator提供了实现迭代器的快捷方法。
⚠️:根据抽象基类: Generator继承自Iterator。但只是抽象的。
方法:
定义一个函数,在内部使用yield,这个函数就是一个generator函数,它是函数对象。类是<class function>。
用它创建的对象,就是生成器对象。generator 对象。reverse(10).__class__是<class 'generator'>
def reverse(data): for index in range(len(data)-1, -1, -1): yield data[index] >>> for char in reverse('golf'): ... print(char) ... f l o g
生成器表达式:
一个更简便的方法定义generator。
>>> g = (x * x for x in range(10)) >>> g <generator object <genexpr> at 0x1022ef630> >>> next(g) 0 >>> next(g) 1
每调用一次next,就是循环一次。
例子,演示定义一个生成可迭代对象的类:
#linshi.py class Reverse: """Iterator for looping over a sequence backwards.""" def __init__(self, data): self.data = data self.index = len(data) def __iter__(self): return self def __next__(self): if self.index == 0: raise StopIteration self.index = self.index - 1 return self.data[self.index]
然后:
>>> import linshi.py >>> import linshi >>> rev = linshi.Reverse('spam') >>> rev <linshi.Reverse object at 0x107c01430> >>> from collections.abc import Iterable >>> isinstance(rev, Iterable) True
由此可知,定义的rev类的对象是可迭代的对象。
collections.abc ---容器的抽象基类
这个模块定义了一些抽象基类。它们可用于判断一个具体类是否具有某一特定的接口;例如,这个类是否可哈希,或其是否为映射类。
abc是abstract base class的简写。是鸭子类型duck-typing的补充。
ABC 引入了虚拟子类,这种类并非继承自其他类,但却仍能被 isinstance()
和 issubclass()
所认可;详见 abc
模块文档。
class collections.abc.
Iterable
使用 isinstance(obj, Iterable)
可以检测一个类是否已经注册到了 Iterable
或者实现了 __iter__()
函数。
例子:
>>> from collections.abc import Iterator >>> isinstance([1,2], Iterable) True
list实例并非继承自collections.abc.Iterable类。但是Iterable类是一个抽象基类。它提供了接口,用于判断list是否可迭代。
listl类有__iter__方法, 因此可以判断它是可迭代的。
itertools模块
itertools 模块中主要包含了一些用于生成迭代器的函数。
例子:
>>> import collections.abc as abc >>> import itertools as it >>> it.count <class 'itertools.count'> >>> a = it.count(10.3) >>> a count(10.3) >>> abc.Iterable <class 'collections.abc.Iterable'> >>> isinstance(a, abc.Iterable) True >>> isinstance(a, abc.Iterator) True
解释:使用count()生成的对象a是可迭代的,同时也是迭代器。
itertools
模块提供的全部是处理迭代功能的函数,它们的返回值不是list,而是Iterator
,只有用for
循环迭代的时候才真正计算。
(延伸)duck-typing
指一种编程风格,它并不依靠查找对象类型来确定其是否具有正确的接口,而是直接调用或使用其方法或属性。
“看起来像鸭子,叫起来也像鸭子,那么肯定就是鸭子。”
由于强调接口而非特定的类型,设计良好的代码可通过允许"多态替代"polymorphic substitution来提升灵活性。
高级的动态语言都有这种编程风格,这和静态语言如java是不一样的。(摘录:Ruby元编程P116)
在静态语言里,说对象的类型是T,是因为它属于T类(或是因为它实现了接口T),而在Ruby这样的动态语言,对象的"类型"并不严格的和它的类相关,"类型"只是对象能相应的一组方法。这种概念就是duck-typing。
duck-typing编程风格可以归类到多态中去。
例子:
class Eg1: def __init__(self, text): self.text = text self.sub_text = text.split(' ') def __getitem__(self, index): return self.sub_text[index] def __len__(self): return len(self.sub_text) o1 = Eg1('Hello, the wonderful new world!') print('长度:', len(o1)) for i in o1: print(i)
类Eg1。它的对象可以计算长度,可以循环,这是因为它通过object.__len__, object.__getitem__实现了相应的协议。这种无需关注它的类型,而只注重接口的编码风格,就是duck-typing。
本例子:通过在类中定义了__getitem__方法,实例o1可以:
- 做取值运算,o1[0], []就是一个语法糖。相当于调用o1.__getitem(0),或type(o1).__getitem__(o1, 0)
- 通过__getitem__,让o1支持序列协议sequence protocol。 可以使用iter(object)方法。
⚠️,实现了__getitem__和__len__就会被认为是序列。
继承和多态
例子:
动物->鸭子,鸭子可以分为绿头鸭,黄鸭等等。这是继承。
一只黄鸭的类型是黄鸭,它也是鸭子,也是动物。
多态:
对于一个实例对象,开发者只需要知道它是鸭子类型,就可以使用鸭子类型的各种实例方法,比如swimming()方法。无需知道它的子类型,即是黄鸭还是绿头鸭的问题。
只有在运行代码时,后台代码才会自动判断它是什么子类型,然后沿着继承链条,找到鸭子类中的实例方法。
重写方法:
父类鸭子类,有swimming()方法,绿头鸭类,可以在自身重写swimming()方法,这样绿头鸭的实例就只会调用绿头鸭类的swimming()方法。当然可以在这个方法内使用super调用父类的swimming()方法,并对其修改。形成自身的swimming方法。