python3:iterable, iterator, generator,抽象基类, itertools的使用。

目录:

  • 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)

每调用一次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方法。


原文地址:https://www.cnblogs.com/chentianwei/p/11928966.html