Python中的容器(container)、迭代器(iterator)和生成器(generator:yield)

版权声明:原创文章,转载请注明出处! https://blog.csdn.net/L_15156024189/article/details/84854171

1、容器迭代器iterator 

以list容器(tuple,range,file类似)为例,list容器可以调用自己的__iter__()方法获取用来迭代自己内部元素的迭代器。例如:

# list容器
myList = [1, 2, 3]
# 获取list容器的迭代器
it = myList.__iter__()

 示例中首先给出了一个myList容器对象,内部存放了3个整数,该容器对象通过__iter__()方法获取了一个迭代器it。下面我们就可使用it迭代器来迭代myList中的元素了。

2、迭代容器元素

上面获得的it迭代器中有两个方法:__next__()和__iter__()。

(1)__iter__()

__iter__()方法返回迭代器自己,也就是it,例如:

# 迭代器可以自己返回自己
that = it.__iter__()
print((it == that) and (it is that))

迭代器it又通过调用自己的方法__iter__(),获得了一个新的迭代器that,那么他们是否是一样的呢?运行上面结果,如下:

True

 “==”用来判断内容是否相等,“is”用来判断对象是否相同,“and”是逻辑与操作。

(2)__next__()

__next__()方法从容器第一个元素开始,每次调用向后迭代一个元素并返回该元素,例如:

# 第一种迭代方式:迭代list容器元素
try:
    s = it.__next__()
    while s:
        print(s)
        s = it.__next__()
except StopIteration:
    pass

程序运行结果:

1
2
3

 示例中,it迭代器通过调用__next__()方法遍历出myList容器中的每个元素。读者注意到,在示例中,我们使用了try except语句捕获了StopIteration,原因是__next__()方法迭代完容器后,会raise StopIteration,它也是容器迭代完的标志。我们也可以使用next()方法,将迭代器it作为参数来遍历容器,如下:

# 重置迭代器
it = mylist.__iter__()
# 第二种迭代方式
try:
    s = next(it)
    while s:
        print(s)
        s = next(it)
except StopIteration:
    pass

 

3、简化迭代

(1) for in

如果每次都按照如上方式迭代,是不是非常麻烦,Python为我们提供了for in语句简化了迭代,例如:

for element in mylist:
    print(element)

输出结果:

1
2
3

 使用for in语句是不是非常便利,也不用再关心StopIteration了。

(2)其他容器迭代

# list容器迭代
for element in [1, 2, 3]:
    print(element)

# tuple元组迭代
for element in (1, 2, 3):
    print(element)

# map映射(或称字典)迭代
for key in {'one':1, 'two':2}:
    print(key)

# 字符串迭代
for char in "123":
    print(char)

# 文件迭代
# open("myfile.txt")是以读模式打开一个文件
for line in open("myfile.txt"):
    print(line, end='')

4、自定义容器

容器的标记是需要实现__next__()和__iter__()两个方法,如下:

class Fab(object):
    '''
    斐波那契数列:后一个数是前两个数的和
    例如:1,1,2,3,5,8,……
    '''

    def __init__(self, fab_len):
        '''
        数列长度
        :param fab_len: 数列长度
        '''
        self.len = fab_len
        self.count, self.a, self.b = 0, 0, 1

    def __iter__(self):
        '''
        返回迭代器自身
        :return: 返回迭代器自身
        '''
        return self

    def __next__(self):
        self.r = self.b
        if self.count <= self.len:
            self.a, self.b = self.b, self.a + self.b
            self.count += 1
            return self.r
        raise StopIteration

示例中,我们定义了一个Fab容器,容器中存储斐波那契数列,其中含有含有三个方法:__init__()、__iter__()和__next__()。

__init__()方法初始化Fab容器,包括容器大小,斐波那契数列的开始元素a,b。后两个方法就不用再解释了。下面我们使用这个容器迭代斐波那契数列,如下:

# 自定义容器
myContainer = Fab(10)

# 获取迭代器
it = myContainer.__iter__()
try:
    s = it.__next__()
    while s:
        print(s, end=',')
        s = it.__next__()
except StopIteration:
    pass

程序运行结果:

1,1,2,3,5,8,13,21,34,55,89,

 我们同样可以使用for in语句对自定义的容器遍历,如下:

for e in Fab(10):
    print(e, end=',')

程序运行结果:

1,1,2,3,5,8,13,21,34,55,89,

5、生成器

我们看到,自定义的容器需要单独定义一个类,并实现__next__()和__iter__()方法后才可以使用,这貌似有些麻烦,有没有更简单的方法呢?当然有,下面我们引入Python的生成器,通过使用关键字yield重新实现斐波那契数列容器。首先,我们看下面一个例子,如下:

程序运行结果如下: 

count start
while start
before yield: n= 3
i= 3

after yield: n= 3
current while end n= 2
while start
before yield: n= 2
i= 2

after yield: n= 2
current while end n= 1
while start
before yield: n= 1
i= 1

after yield: n= 1
current while end n= 0
count end

Process finished with exit code 0

 该示例中使用yield关键字,根据笔者的执行步骤标记,读者应该可以理解程序的运行过程。yield有屈服和放弃之意。熟悉Java多线程编程的读者,对此并不陌生。意思是当程序运行到yield语句时,放弃count方法之后语句的运行(同时会保存数据,以便之后从该出继续执行),转而继续执行for循环,第一次for执行完,又继续回到count方法,但这次不是从头开始,而是从上一次放弃的位置继续运行。这里很有Java多线程的感觉,一个线程运行for,另一个线程运行count方法,当线程运行到yield时,该线程进入阻塞,唤醒另一个线程for执行,如此下去……。

斐波那契数列可以通过如下生成器简化:

# 代码4
def fab(len):
    count, a, b = 0, 0, 1
    while count < len:
        yield b
        a, b = b, a + b
        count += 1


# 执行
for e in fab(10):
    print(e,end=',')

相比于自定义容器是不是超简洁。哈哈!

猜你喜欢

转载自blog.csdn.net/L_15156024189/article/details/84854171