zoukankan      html  css  js  c++  java
  • 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:Python27python.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

     

      

  • 相关阅读:
    STM32时钟树
    js jQuery函数 $.ajax()
    jQuery 语法
    jQuery介绍
    python笔记2 生成器 文件读写
    python笔记1,语法,函数,类和实例,异常
    Scrapy爬虫入门系列4抓取豆瓣Top250电影数据
    Scrapy爬虫入门系列3 将抓取到的数据存入数据库与验证数据有效性
    opus 规范 与参数解析
    开源播放器ijkplayer源码结构
  • 原文地址:https://www.cnblogs.com/rsdqc/p/5185755.html
Copyright © 2011-2022 走看看