Python进阶之路 迭代器转换为列表、创建生成器、递归生成器

迭代器转换为列表

尽管迭代器很好用,但仍然不具备某些功能,例如,通过索引获取某个元素,进行分片操作。这些操作都是列表的专利,所以在很多时候,需要将迭代器转换为列表。但有很多迭代器都是无限迭代的。因此,在讲迭代器转换为列表时,需要给迭代器能够迭代的元素限定一个范围,否则内存就会溢出。要想让迭代器停止迭代,值需要抛出StopIteration异常即可。通过list函数可以直接将迭代器转换为列表。

[例 10.11] 本例会将斐波那契数列迭代器通过list函数转换为列表。斐波那契数列迭代器限制了最大迭代值不能超过500。


class Fibonacci:
    def __init__(self):
        self.a = 0
        self.b = 1
    def __next__(self):
        result = self.a
        self.a, self.b = self.b,self.a + self.b
        
        if result > 500: raise StopIteration
        return result
    def __iter__(self):
        return self

fibs1 = Fibonacci()

print(list(fibs1))
fibs2 = Fibonacci()

for fib in fibs2:
    print(fib,end = ' ')

输出结果:

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 

从上面的代码可以看出,尽管在__next__方法中,当result大于500时抛出了Stoperation异常,但这个异常是在迭代的过程中由系统处理的,并不会在程序中抛出,所以要想将无线迭代改成有限迭代,可以在适当的时候抛出Stoperation异常。

创建生成器

要定义一个生成器,首先需要定义一个函数,在该函数中对某个集合或迭代器进行迭代,然后使用yield语句产生当前要生成的值,这时函数会被冻结,直到调用生成器的代码继续迭代下一个值,生成器才会继续执行。


def mygenerator():
    numlist = [1,2,3,4,5,6,7,8,9]
    for num in numlist:
        
        yield num
    
for num in mygenerator():
    print(num,end = ' ')

输出结果:

1 2 3 4 5 6 7 8 9 

如果将yield num换成print(num)就非常容易理解了,对numlist列表进行迭代,并输出该列表中每一个元素值。不过这里使用了yield语句来提交当前生成的值,也就是for循环中num的值,然后mygenerator函数会冻结(暂时不再往下执行了),知道for循环继续下一次循环,再次对mygenerator函数进行迭代,mygenerator函数才会继续执行,继续使用yield语句提交下一个要生成的值,知道numlist列表中的最后一个元素为止。从这一点可以看出,生成器函数是惰性的,在迭代的过程中,每取一个值,生成器函数就往下执行一步。

[例 10.12] 本例会利用生成器将一个二维的列表转换为一维的列表。

nestedlist = [[1,2,3],[4,3,2],[1,2,4,5,7]]

def enumlist(nestedlist):
    
    for sublist in nestedlist:
        
        for element in sublist:
           
            yield element

for num in enumlist(nestedlist):
    print(num,end=' ')
print()

numlist = list(enumlist(nestedlist))
print(numlist)
print(numlist[1:4])

输出结果:

1 2 3 4 3 2 1 2 4 5 7 
[1, 2, 3, 4, 3, 2, 1, 2, 4, 5, 7]
[2, 3, 4]

递归生成器

对一个二维列表进行了一维化处理。要想对三维、四维甚至更多维的列表进行一维化处理,可以采用递归的方式进行处理。处理的方式是先对多维列表进行迭代,然后判断每个列表元素是否还是列表:如果仍然是列表,则继续对这个列表进行迭代;如果只是一个普通的值,则使用yield语句返回生成的值。

[例 10.13] 本例会利用生成器函数将一个多维列表进行一维化处理。


def enumlist(nestedlist):
    try: 
        for sublist in nestedlist:
            for element in enumlist(sublist):
                yield element
    except TypeError:
        yield nestedlist

nestedlist = [4,[1,2,[3,5,6]],[4,3,[1,2,[4,5]],2],[1,2,4,5,7]]
for num in enumlist(nestedlist):
    print(num,end=' ')

输出结果:

4 1 2 3 5 6 4 3 1 2 4 5 2 1 2 4 5 7 

如果多维列表中的某个元素值是字符串类型,那么也会进行迭代,原因是字符串可以看作字符的列表。因为希望将字符串作为一个整体输出,所以在进行迭代之前,先要判断当前元素值是不是字符串类型,如果是字符串类型,则直接通过yield语句返回即可。判断一个值是否是字符串的最简单方法就是使用try语句。因为只有字符串才能与另一个字符串进行连接(使用’+'运算符),所以一个非字符串类型的值与字符串相加一定会抛出异常,这样就很容可以判断一个字符串相加的另一个值是否为字符串类型。

[例 10.14] 本例会改进10.13中的生成器函数,如果列表元素是字符串,会直接将字符串当做单个值返回。


def enumlist(nestedlist):
    try:
        try:nestedlist + '' 
        except TypeError:
            pass    
        else:
            raise TypeError
        
        for sublist in nestedlist:
            for element in enumlist(sublist):
                yield element
    except TypeError:
        yield nestedlist
nestedlist = ['a',['b',['c'], 20,123,[['hello world']]]]
for num in enumlist(nestedlist):
    print(num,end=' ')

输出结果:

a b c 20 123 hello world

你可能感兴趣的:(Python,Python进阶之路)