您的位置:首页 > 编程语言 > Python开发

Python基础教程----迭代器和生成器,递归,八皇后(2)

2017-07-06 15:40 489 查看

6.迭代器

特殊方法__iter__,是迭代器规则的基础

6.1迭代器规则

迭代的意思是重复做一些事很多次,就像在循环中做的那样,但现在为止只是在for循环中对序列和字典进行迭代,但实际上也能对其他对象进行迭代,实现__iter__方法的对象,

    __iter__方法返回一个迭代器,所谓的迭代器就是具有next方法(这个方法在调用时不需要任何参数)的对象,在调用next方法时,迭代器会返回它的下一个值,如果next方法被调用,但迭代器没有值可以返回,就会引发一个StopIteration异常

      迭代规则的关键是什么?如果可以一个接一个地计算值的函数,那么在使用时可能是计算一个值时获取一个值,而不是通过列表一次性获取所有值,如果有很多值,列表就会占用太多的内存,还有其他理由:使用迭代器更通用,简单优雅,

迭代器实现斐波那契数列

Class Fibs:

Def __init__(self):

   Self.a = 0

   Self.b = 0 

Def next(self):

   Self.a, self.b = slef.b, self.a + self.b

   Return self.a

Def __iter__(self):

   Return self

迭代器实现了__iter__方法,这个方法实际上返回迭代器本身,在很多情况下,__iter__回放到其他的会在for循环中使用的对象中,这样一来,程序就能返回所需的迭代器,此外,推荐使用迭代器实现它自己的__iter__方法,然后就能直接在for循环中使用迭代器本身了

一个实现了__iter__方法的对象是可迭代的,一个实现了next方法的对象则是迭代器

首先,产生一个Fibs对象

>>> fibs = Fibs()

可在for循环中使用该对象---比如去查找在斐波那契数列中比1000大的数中的最小的数

>>> for f in fibs:

       If f > 1000:

          Print f

            Break

.....

1597

因为设置了break,所以循环在这里停止了,否则会一直继续下去

内建函数iter可以从可迭代的对象中获得迭代器

>>> it = iter([1, 2, 3])

>>> it.next()              1

>>> it.next()               2

6.2 从迭代器得到序列

除了在迭代器和可迭代对象上进行迭代,还能把它们转换为序列,在大部分能使用序列的情况下(除在索引或分片等操作中),能使用迭代器(或可迭代对象)替换,关于这个一个很有用的例子是使用list构造方法显式地将迭代器转化为列表

>>> class TestIterator:

       value = 0

       def next(self):

          self.value += 1

          if self.value > 10: raise StopIteration

          return self.value

       def __iter__(self):

          return self

>>> t1 = TestIterator()

>>> list(t1)        [1,2,3,4,5,6,7,8,9,10]

7.生成器

也叫简单生成器,是一种用普通的函数语法定义的迭代器

7.1创建生成器

首先创建一个展开嵌套列表的函数,参数是一个列表,

Nested = [[1,2], [3, 4], [5]]

换句话说就是一个列表的列表,函数应该按照顺序打印出列表中的数字

Def flatten(nested):

   For sublist in nested:

     For element in sublist:

        Yield element

首先迭代提供的嵌套列表中的所有自列表,然后按顺序迭代子列表中的元素

如果最后一行是print Element则容易理解了

包含yield语句的函数成为生成器,吹了名字不同,他的行为和普通函数也差很大

它不是像return那样返回值,而是每次产生多个值,每次产生一个值函数就会被冻结,即函数停在那点等待被激活,函数被激活后就从停止点开始执行

接下来可以通过在生成器上迭代来使用所有的值

>>> nested = [[1, 2], [3, 4], [5]]

>>> for num in flatten(nested)

        Print num

1

2

3

4

5

 

or

>>> list(flatten(nested))

[1,2,3,4,5]

7.2 递归生成器

任意层第嵌套

Def flatten(nested):

   Try:

     For sublist in nested:

        For element in flatten(sublist):

           Yield element

   Except TypeError:

     Yield nested

当flatten被调用时,有两种可能性(处理递归时大部分都是两种情况):基本情况和需要递归的情况,在基本情况中,函数被告知展开一个元素(比如一个数字),这种情况下,for循环会引发一个TypeError异常(因为试图对一个数字进行迭代),生成器会产生一个元素。

如果展开的是一个列表(或其他可迭代对象),那么就要进行特殊处理,程序必须遍历所有子列表(一些可能不是列表),并对他们调用flatten,然后使用另一个for循环来产生被展开的子列表中的所有元素,

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

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

这么做只有一个问题,如果nested是一个类似于字符串的对象(字符串,Unicode,UserString,等)那么她就是一个序列,不会引发TypeError

不应该在flatten函数中对类似于字符串的对象进行迭代,出于两个主要原因,首先需要实现的是将类似于字符串的对象当成原子值,而不是当成应被展开的序列,其次对它们进行迭代实际上会导致无穷递归,因为一个字符串的第一个元素是另一个长度为1的字符串,而长度为1的字符串的第一个元素就是字符串本身

为了处理这种情况,则必须在生成器的开始处添加一个检查语句,将传入的对象和一个字符串拼接,看看会不会出现TypeError,这是检查一个对象是不是类似于字符串的最简单最快速的方法

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

如果表达式nested+’’引发了一个TypeError,它就会被忽略,然而如果没有引发TypeError,那么内存try语句中的else子句就会引发一个它自己的TypeError异常。这就会按照原来的样子生成类似于字符串的对象(在except字句的外面)

>>> list(flatten([‘foo’, [‘bar’, [‘baz’]]]))

[‘foo’, ‘bar’, ‘baz’]

上面的代码没有执行类型检查,这里没有测试nested是否是一个字符串(可以使用ininstance函数完成检查),而只是检查nested的行为是不是像一个字符串(通过和字符串拼接来检查)

7.3通用生成器

   生成器是一个包含yield关键字的函数,当它被调用时,在函数体中的代码不会被执行,而会返回一个迭代器,每次请求一个值,就会执行生成器中的代码,直到遇到一个yield或者return语句,yield语句意味着应该生成一个值,return语句意味着生成器要停止执行(不再生成任何东西,return语句只在一个生成器中使用时才能进行无参数调用)

生成器由两部分组成:生成器的函数和生成器的迭代器,生成器的函数时用def语句定义的,包含yield的部分,生成器的迭代器是这个函数返回的部分,按照一钟不是很准确的说法,两个实体经常被当做一个,合起来叫做生成器

>>> def simple_generator():

        yield

>>> simple_generator

<function simple_generator at 153b44>

>>> simple_generator()

<generator object at 1510b0>

生成器的函数返回的迭代器可以像其他的迭代器那样使用

7.4生成器方法

生成器的新属性是在开始运行后位生成器提供值的能力,表现为生成器和‘外部世界’进行交流的渠道

外部作用域访问生成器的send方法,就像访问next方法一样,只不过前者使用一个参数(要发送的‘消息’--任意对象)

在内部则挂起生成器,yield现在作为表达式而不是语句使用,换句话说,当生成器重新运行的时候,yield方法返回一个值,也就是外部通过send方法发送的值,如果next方法被使用,那么yield方法返回None

使用send方法只有在生成器挂起后才有意义(也就是yield函数第一次被执行之后),如果在此之前需要给生成器提供更多信息,只需使用生成器函数的参数

如果真想对刚刚启动的生成器使用send方法,可以将None作为参数进行调用

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息