•   欢迎来到21NN网.
  •   请记住本站网址www.21nn.cn

Python中迭代器和生成器以及列表的引见(附代码)【Python教程】,python

摘要: 本篇文章给人人带来的内容是关于Python中迭代器和生成器以及列表的引见(附代码),有肯定的参考价值,有须要的朋侪能够参考一下,愿望对你有所协助。迭代器在Python2.2版本中被到场,...
本篇文章给人人带来的内容是关于Python中迭代器和生成器以及列表的引见(附代码),有肯定的参考价值,有须要的朋侪能够参考一下,愿望对你有所协助。

迭代器在 Python 2.2 版本中被到场, 它为类序列对象供应了一个类序列的接口。 Python 的迭代无缝地支撑序列对象, 而且它还许可迭代非序列范例, 包括用户定义的对象。即迭代器能够迭代不是序列但表现出序列行动的对象, 比方字典的 key , 一个文件的行, 等等。迭代器有以下特征:

  • 供应了可扩大的迭代器接口.

  • 对列表迭代带来了机能上的加强.

  • 在字典迭代中机能提拔.

  • 建立真正的迭代接口, 而不是本来的随机对象接见.

  • 与一切已存在的用户定义的类以及扩大的模仿序列和映照的对象向后兼容

  • 迭代非序列鸠合(比方映照和文件)时, 能够建立更简约可读的代码.

迭代器对象即完成了迭代器协定的对象,在 Python 中,支撑迭代器协定就是完成对象的 __iter__() 和 next() 要领(注:在 Python3 中被改成 next 要领)。个中 __iter__() 要领返回迭代器对象自身;next() 要领返回容器的下一个元素,在末端时激发 StopIteration 异常。

迭代器协定

迭代器协定即完成 __iter__() 与 next() 要领。这两个要领是迭代器最基础的要领,一个用来取得迭代器对象,一个用来猎取容器中的下一个元素。关于可迭代对象,能够运用内建函数 iter() 来猎取它的迭代器对象:

li = [1, 2]
it = iter(li)
print it

print it.next()
print it.next()
print it.next()

效果以下所示:

<listiterator object at 0xb708aa6c>
1
2
Traceback (most recent call last):
  File "iter.py", line 21, in <module>
    print it.next()
StopIteration

列表(list)自身是可迭代的,经由历程 iter() 要领能够取得其迭代器对象,然后就能够经由历程 next() 要领来接见 list 中的元素。当容器中没有能够接见的元素时, next() 要领将会抛出一个 StopIteration 的异常,从而住手迭代器。当我们运用 for 语句的时刻,for 语句就会自动的经由历程 __iter__() 要领来取得迭代器对象,而且经由历程 next() 要领来猎取下一个元素,碰到 StopIteration 异常时会自动完毕迭代。

自定义迭代器

本身建立迭代器实际上就是完成一个带有 __iter__() 要领和 next() 要领的类,用该类建立的实例等于可迭代对象。比方我们用迭代器来完成斐波那契数列:

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1

    def next(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

    def __iter__(self):
        return self

fibs = Fibs()  // 这将获得一个无穷的数列
for f in fibs:
    if f > 1000:
        print f
        break
    else:
        print f

这里有一个题目,大多数的序列或许类序列都不是无穷的,所以在到达肯定前提后就该住手。因而我们须要在序列或许类序列须要完毕时激发 StopIteration 异常:

class MyRange(object):
    def __init__(self, n):
        self.idx = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.idx < self.n:
            val = self.idx
            self.idx += 1
            return val
        else:
            raise StopIteration()

myRange = MyRange(3)
for i in myRange:
        print i

可迭代对象和迭代器对象

可迭代对象即具有 __iter__() 要领的对象,该要领可猎取其迭代器对象。迭代器对象即具有 next() 要领的对象。也就是说,一个完成了 __iter_() 的对象是可迭代的,一个完成了 next() 要领的对象则是迭代器。可迭代对象也能够是迭代器对象,如文件对象。此时可迭代对象本身有 next() 要领,而其 __iter() 要领返回的就是它本身。关于许多内置对象及其派生对象,如 list、dict 等,由于须要支撑屡次翻开迭代器,因而本身并不是迭代器对象,须要用 __iter_() 要领返回其迭代器对象,并用迭代器对象来接见别的元素。

以上例子中的 myRange 这个对象就是一个可迭代对象,同时它自身也是一个迭代器对象。关于一个可迭代对象,假如它自身又是一个迭代器对象,就会有如许一个题目,其没有办法支撑屡次迭代。以下所示:

myRange = MyRange(3)
print myRange is iter(myRange)

print [i for i in myRange]
print [i for i in myRange]

运转效果:

True
[0, 1, 2]
[]

为了处置惩罚上面的题目,能够离别定义可迭代范例对象和迭代器范例对象;然后可迭代范例对象的 __iter__() 要领能够取得一个迭代器范例的对象。以下所示:

class Zrange:
    def __init__(self, n):
        self.n = n

    def __iter__(self):
        return ZrangeIterator(self.n)

class ZrangeIterator:
    def __init__(self, n):
        self.i = 0
        self.n = n

    def __iter__(self):
        return self

    def next(self):
        if self.i < self.n:
            i = self.i
            self.i += 1
            return i
        else:
            raise StopIteration()    

zrange = Zrange(3)
print zrange is iter(zrange)         

print [i for i in zrange]
print [i for i in zrange]

运转效果:

False
[0, 1, 2]
[0, 1, 2]

别的, reversed() 内建函数将返回一个反序接见的迭代器,enumerate() 内建函数一样也返回迭代器。比方能够用 enumerate() 函数遍历列表:

ll = [1, 2, 3]
print enumerate(ll)

/* 文雅的遍历列表 */
for i, ele in enumerate(ll):
    print i, ll[i]

生成器

迭代器和生成器多是近几年引入的最壮大的两个特征。生成器是一种用一般的函数语法定义的迭代器,也就是说生成器实际上就是一个函数。然则生成器不必 return 返回,而是用 yield 一次返回一个效果,在每一个效果之间挂起和继承它们的状况,来自动完成迭代协定。任何包括 yield 语句的函数称为生成器。yield 被人们文雅的称之为语法糖,意义就是包在里边的甜心。在 yield 的内部是一个状况机,维护着挂起和继承的状况。

生成器实行流程

先看看下边的列子:

def Zrange(n):
    print "beginning of Zrange"
    i = 0
    while i < n:
        print "before yield", i
        yield i
        i += 1
        print "after yield", i

    print "endding of Zrange"

zrange = Zrange(3)
print "------------"

print zrange.next()
print "------------"

print zrange.next()
print "------------"

print zrange.next()
print "------------"

print zrange.next()def flatten(nested):
    result = []
    try:
        # 不要迭代类似字符串的对象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)
    except TypeError:
        result.append(nested)
    return result
print "------------"

实行效果:

--------------------
beginning of Zrange
before yield 0
0
--------------------
after yield 1
before yield 1
1
--------------------
after yield 2
before yield 2
2
--------------------
after yield 3
endding of Zrange
Traceback (most recent call last):
  File "one.py", line 38, in &lt;module>
    print zrange.next()
StopIteration

经由历程效果能够看到:

  • 当挪用生成器函数的时刻,函数只是返回了一个生成器对象,并没有 实行。

  • 当next()要领第一次被挪用的时刻,生成器函数才最先实行,实行到yield语句处住手

  • next()要领的返回值就是yield语句处的参数(yielded value)

  • 当继承挪用next()要领的时刻,函数将接着上一次住手的yield语句处继承实行,并到下一个yield处住手;假如背面没有yield就抛出StopIteration异常

递归生成器

生成器能够向函数一样举行递归运用,下面枚举两个示例:

对一个序枚举行全分列:

def permutations(li):
    if len(li) == 0:
        yield li
    else:
        for i in range(len(li)):
            li[0], li[i] = li[i], li[0] #
            for item in permutations(li[1:]):
                yield [li[0]] + item

for item in permutations(range(3)):
    print item

这里的完成思绪是,每次取序列中不一样的元素放在最前面,直到只要一个元素时返回,并将返回效果今后拼接。

睁开多层嵌套的列表:

def flatten(nested):
    try:
        # 不要迭代类似于字符串的对象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                yield element
    except TypeError:
        yield nested

print list(flatten([[[1], 2], 3, 4, [5, [6, 7]], 8]))

这里须要注重的是,不该该在 flatten 函数中对类似于字符串的对象举行迭代,如许会致使无穷递归,由于一个字符串的第一个元素是另一个长度为1的字符串,而长度为一个字符串的第一个元素就是字符串自身。

通用生成器

生成器能够工资是由两部份构成:生成器的函数和生成器的迭代器。生成器的函数是用 def 语句定义的,包括 yield 部份,生成器的迭代器是这个函数返回的部份。根据一种不是很正确的说法,两个实体经常被当作一个,合起来叫做生成器。以下实例所示:

def simple_generator():
    yield 1

print simple_generator
print simple_generator()

运转效果:

<function simple_generator at 0xb743d79c>
<generator object simple_generator at 0xb71c7be4>

生成器要领

  • send(value)

外部作用域接见生成器的 send 要领,就像接见 next() 要领一样。next()要领能够恢复生成器状况并继承实行,实在 send() 是除 next() 外另一个恢复生成器的要领。Python 2.5 中,yield 语句变成了 yield 表达式,也就是说 yield 能够有一个值,而这个值就是send()要领的参数,所以 send(None) 和 next() 是等效的。一样,next()和send()的返回值都是 yield语 句处的参数(yielded value)。运用 send() 要领只要在生成器挂起以后才有意义,假如真想对方才启动的生成器运用 send 要领,则能够将 None 作为参数举行挪用。也就是说, 第一次挪用时,要运用 next() 语句或 send(None),由于没有 yield 语句来吸收这个值

  • throw()

用于在生成器内激发一个异常。

  • close()

用于住手生成器,挪用它时,会在 yield 运转出激发一个 GeneratorExit 异常。

运用示例:

def Zrange(n):
    i = 0
    while i < n:
        val = yield i
        print "val is", val
        i += 1

zrange = Zrange(5)

print zrange.next()
print zrange.next()
print zrange.send("hello")
print zrange.next()
#print zrange.next()

zrange.close()

print zrange.send("world")

模仿生成器

在旧的 Python 版本中并不支撑生成器,那末我们能够用一般的函数来模仿生成器。以下所示:

def flatten(nested):
    result = []
    try:
        # 不要迭代类似字符串的对象
        try: nested + ""
        except TypeError: pass
        else: raise TypeError
        for sublist in nested:
            for element in flatten(sublist):
                result.append(element)
    except TypeError:
        result.append(nested)
    return result

只管这个版本能够不适用于一切的生成器,但对大多数生成器来说是可行的。比方,它不适用于一个无穷的生成器。

列表剖析和生成器表达式

列表剖析

列表剖析( List comprehensions, 或缩略为 list comps ) 来自函数式编程言语 Haskell . 它是一个异常有用, 简朴, 而且天真的东西, 能够用来动态地建立列表。其语法构造为:

[expr for iter_var in iterable]

这个语句的中心是 for 轮回, 它迭代 iterable 对象的一切条目. 前边的 expr 运用于序列的每一个成员, 末了的效果值是该表达式发生的列表。 迭代变量并不须要是表达式的一部份。比方用 lambda 函数盘算序列成员的平方的表达是为:

map(lambda x: x ** 2, range(6))

这能够用列表剖析来改写:

[x ** 2 for x in range(6)]

列表剖析的表达式能够庖代内建的 map() 函数以及 lambda , 而且效力更高。连系 if 语句,列表剖析还供应了一个扩大版本的语法:

[expr for iter_var in iterable if cond_expr]

这个语法在迭代时会过滤/捕捉满足前提表达式 cond_expr 的序列成员。比方挑选出序列中的奇数能够用下边的要领:

[x for x in seq if x % 2]

列表剖析另有许多奇妙的运用:

迭代一个有三行五列的矩阵:

[(x+1,y+1) for x in range(3) for y in range(5)]

盘算出一切非空缺字符的数量:

f = open('hhga.txt', 'r')

len([word for line in f for word in line.split()])

生成器表达式

生成器表达式是列表剖析的一个扩大。列表剖析的一个不足就是必要生成一切的数据, 用以建立全部列表。这能够对有大批数据的迭代器有负面效应。生成器表达式经由历程连系列表剖析和生成器处置惩罚了这个题目。生成器表达式在 Python 2.4 被引入, 它与列表剖析异常类似,而且它们的基础语法基础雷同; 不过它并不真正建立数字列表, 而是返回一个生成器,这个生成器在每次盘算出一个条目后,把这个条目“发生”(yield)出来。生成器表达式运用了"耽误盘算"(lazy evaluation), 所以它在运用内存上更有用。生成器表达式语法:

(expr for iter_var in iterable if cond_expr)

生成器并不会让列表剖析烧毁, 它只是一个内存运用更友爱的构造, 基于此, 有许多运用生 成器处所,以下所示:

疾速地盘算文件大小:

上面我们用列表剖析盘算出了文件中非空缺字符的数量,那末只要用 sum() 函数对每一个单词的长度乞降,则可大抵盘算出文件的大小。sum() 函数的参数不仅能够是列表,还能够是可迭代对象,比方生成器表达式。这里我们用生成器表达式改写全部历程:

sum(len(word) for line in data for word in line.split())

交织配对:

生成器表达式就好像是懒散的列表剖析(这反而成了它重要的上风)。它还能够用来处置惩罚其他列表或生成器:

rows = [1, 2, 3, 17]

def cols(): # example of simple generator
    yield 56  
    yield 2  
    yield 1

x_product_pairs = ((i, j) for i in rows for j in cols())

for pair in x_product_pairs:
    print pair

寻觅文件最长的行:

def longest(filename):
    glines = (x.strip() for x in open(filename))
    return max([len(l) for l in glines])

# Script starts from here

if __name__ == "__main__":
    print longest("/etc/hosts")

这个例子摘自 《Python中心编程》 中生成器表达式一节,作者在原书中只用了一行代码来完成这个功用,即:

return max(len(x.strip()) for x in open('/etc/motd'))

这行代码会报出以下毛病:

TypeError: object of type 'generator' has no len()

也就是说生成器没有 len() 要领,所以如许并不可行,然则用列表剖析则能够用一行完成:

return max([len(x.strip()) for x in open("/etc/motd")])

以上就是Python中迭代器和生成器以及列表的引见(附代码)的细致内容,更多请关注ki4网别的相干文章!

分享到:

发表评论

评论列表

还没有评论,快来说点什么吧~

公众号二维码

微信公众号