python——迭代器、生成器、装饰器

迭代器

迭代器规则

  迭代:重复做一些事很多次,就像在循环中那样。

  不仅可以对字典和序列进行迭代,还可以对其他对象进行迭代:只要该对象实现了__iter__方法。

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

  注意:迭代器规则在3.0中有一些变化。在新的规则中,迭代器对象应该实现__next__方法,而不是next。而新的内建函数next方法可以用于访问这个方法。换句话说,next(it)等同于3.0之前版本中的it.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 > 100:
        print(f)
        break
使用迭代器

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

  **正式的说话是,一个实现了__iter__方法的对象是可迭代的,一个实现了next方法的对象则是迭代器。

  **内建函数iter可以从可迭代的对象中获得迭代器。此外,他也可以从函数或者其他可调用的对象中获取可迭代对象。

  从迭代器得到序列

  除了在迭代器和可迭代对象上进行迭代(这是经常做的)外,还能把它们转换为序列。在大部分能使用序列的情况下(除了在索引或者分片等操作中),都能使用迭代器(或者可迭代对象)替换。关于这个的的一个很有用的例子是使用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

ti = TestIterator()
list(ti)

  生成器

  生成器是一种普通的函数定义的迭代器。

 1 def flatten(nested):
 2     for sublist in nested:
 3         for element in sublist:
 4             yield element
 5 
 6 nested = [[1, 2], [3, 4], [5]]
 7 for num in flatten(nested):
 8     print(num)
 9 
10 -----结果
11 D:\Python27\python.exe D:/pythonwork/day04/iterator.py
12 1
13 2
14 3
15 4
16 5

  任何含有yield语句的函数都称为生成器。出名字不同以外,他的行为和普通的函数也是有很大的差别的。这就是它不是像return那样返回值,而是每次产生多个值。每次产生一个值(使用yield语句),函数就会被冻结:即函数停在那点等待被重新唤醒。函数唤醒后就从停止的那点开始执行。

  递归生成器

  要解决多层嵌套循环的问题,需要借助于递归(recursion)。如下:

 1 # 用递归解决多次嵌套循环问题
 2 
 3 
 4 def flatten(nested):
 5     # 处理异常
 6     try:
 7         for sublist in nested:
 8             for element in flatten(sublist):
 9                 yield element                  # 使用yield每次产生一个值,之后函数会被冻结,等待唤醒。
10     except TypeError:
11         yield nested
递归生成器Demo

  解析上述案例:

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

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

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

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

 1 def flatten(nested):
 2     # 处理异常
 3     try:
 4         try: nested + ''
 5         except TypeError: pass
 6         else: raise TypeError
 7         for sublist in nested:
 8             for element in flatten(sublist):
 9                 yield element                  # 使用yield每次产生一个值,之后函数会被冻结,等待唤醒。
10     except TypeError:
11         yield nested
Demo

  对代码进行解析:

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

  通用生成器

  生成器就是一种包含yield关键字的函数。当他被调用时,在函数体中的代码不会被执行,而会返回一个迭代器。每次请求一个值,就会执行生成器中的代码,知道遇到一个yield或者return语句。yield语句以为着应该生成一个值。return语句意味着生成器要停止执行(不再生成任何东西,return语句只有一个生成器中使用时才能进行无参数调用)。
  生成器是由两位部分组成:生成器的函数和生成器的迭代器。生成器的函数是用def语句定义的,包含yield的部分,生成器的迭代器是这个函数返回的部分。生成器的函数返回的迭代器可以像其他的迭代器那样使用。

  生成器方法

  send方法:

  “外部世界”作用域访问生成器的send方法,就像访问next方法一样,只不过前者使用一个参数(要发送的额“信息”——任意对象)。

  yield方法:

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

  注意:

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

 1 def repeater(value):
 2     while True:
 3         new = (yield value)
 4         if new is not None:
 5             value = new
 6 
 7 
 8 r = repeater(43)
 9 print(r.__next__())
10 print(r.send("hello python!"))
View Code

  生成器还有两个其他的方法

  throw方法:使用异常类型调用,还有可选的值以及回溯对象,用于在生成器内引发一个异常(在yield表达式中)。

  close方法:用于停止生成器。

  模拟生成器

  例子如下:

 1 # 下面是flatten生成器用普通函数实现
 2 def flatten(nested):
 3     result = []
 4     try:
 5         # 不要迭代类似字符串的对象
 6         try:
 7             nested + ''
 8         except TypeError: pass
 9         else: raise TypeError
10         for sublist in nested:
11             for element in flatten(sublist):
12                 result.append(element)
13     except TypeError:
14         result.append(nested)
15     return result
flatten生成器用普通函数实现

   装饰器

   见武sir博客:http://www.cnblogs.com/wupeiqi/articles/4943406.html

 

 

 

 

 

 

 

 

  

 

posted on 2016-02-10 14:14  揉碎的青春  阅读(743)  评论(0编辑  收藏  举报

导航