美文网首页Python精选
Python迭代器,生成器和装饰器

Python迭代器,生成器和装饰器

作者: 淡定_蜗牛 | 来源:发表于2019-02-25 17:38 被阅读0次

    Iterators

    遵循迭代器协议,Python迭代器对象需要支持两种方法。

    iter返回迭代器对象本身。这用于forin语句。

    next方法返回迭代器中的下一个值。如果没有更多的项目要返回,那么它应该引发StopIteration异常。

    class Counter(object):
        def __init__(self, low, high):
            self.current = low
            self.high = high
    
        def __iter__(self):
            'Returns itself as an iterator object'
            return self
    
        def __next__(self):
            'Returns the next value till current is lower than high'
            if self.current > self.high:
                raise StopIteration
            else:
                self.current += 1
                return self.current - 1
    

    现在我们可以在代码中使用这个迭代器。

    >>> c = Counter(5,10)
    >>> for i in c:
    ...   print(i, end=' ')
    ...
    5 6 7 8 9 10
    

    请记住,迭代器对象只能使用一次。这意味着在它提升 一次StopIteration之后,它将继续引发相同的异常。

    >>> c = Counter(5,6)
    >>> next(c)
    5
    >>> next(c)
    6
    >>> next(c)
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 11, in next
    StopIteration
    >>> next(c)
    Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<stdin>", line 11, in next
    StopIteration
    

    在我们看到的for循环示例中使用迭代器,下面的示例尝试显示幕后代码。

    >>> iterator = iter(c)
    >>> while True:
    ...     try:
    ...         x = iterator.__next__()
    ...         print(x, end=' ')
    ...     except StopIteration as e:
    ...         break
    ...
    5 6 7 8 9 10
    

    Generators

    在本节中,我们将了解Python生成器。它们是在Python 2.3中引入的。使用函数中的关键字yield创建迭代器是一种更简单的方法。

    >>> def my_generator():
    ...     print("Inside my generator")
    ...     yield 'a'
    ...     yield 'b'
    ...     yield 'c'
    ...
    >>> my_generator()
    <generator object my_generator at 0x7fbcfa0a6aa0>
    

    在上面的例子中,我们使用yield语句创建一个简单的生成器。我们可以在for循环中使用它,就像我们使用任何其他迭代器一样。

    >>> for char in my_generator():
    ...     print(char)
    ...
    Inside my generator
    a
    b
    c
    

    在下一个示例中,我们将使用生成器函数创建相同的Counter类,并在for循环中使用它。

    def counter_generator(low, high):
        while low <= high:
           yield low
           low += 1
    
    >>> for i in counter_generator(5,10):
    ...     print(i, end=' ')
    ...
    5 6 7 8 9 10
    

    当while循环到达yield语句时,返回low值并暂停生成器状态。在第二次下一次调用期间,生成器恢复到之前冻结的位置,然后将low的值增加1。它继续while循环并再次进入yield语句。

    当您调用生成器函数时,它返回* generator 对象。如果你在这个对象上调用 dir ,你会发现它在其他方法中包含iter __ next __ *方法。

       >>> dir(c)
       ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
    '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__iter__',
    '__le__', '__lt__', '__name__', '__ne__', '__new__', '__next__', '__reduce__',
    '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
    'close', 'gi_code', 'gi_frame', 'gi_running', 'send', 'throw']
    

    我们主要使用生成器进行laze评估。这样,生成器成为处理大量数据的好方法。如果您不想加载内存中的所有数据,可以使用一次生成器,它会一次传递每个数据。

    这种示例的最大例子之一是os.path.walk()函数,它使用回调函数和当前的os.walk生成器。使用生成器实现可以节省内存。

    我们可以有生成无限值的生成器。以下是一个这样的例子。

    >>> def infinite_generator(start=0):
    ...     while True:
    ...         yield start
    ...         start += 1
    ...
    >>> for num in infinite_generator(4):
    ...     print(num, end=' ')
    ...     if num > 20:
    ...         break
    ...
    4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
    

    如果我们回到my_generator的例子,我们会发现生成器的一个特性。它们不可重复使用。

    >>> g = my_generator()
    >>> for c in g:
    ...     print(c)
    ...
    Inside my generator
    a
    b
    c
    >>> for c in g:
    ...     print(c)
    ...
    

    创建可重用生成器的一种方法是基于对象的生成器,它不保持任何状态。任何产生数据的iter方法的类都可以用作对象生成器。在下面的例子中,我们将重新创建计数器生成器。

    >>> class Counter(object):
    ...     def __init__(self, low, high):
    ...         self.low = low
    ...         self.high = high
    ...     def __iter__(self):
    ...          counter = self.low
    ...          while self.high >= counter:
    ...              yield counter
    ...              counter += 1
    ...
    >>> gobj = Counter(5, 10)
    >>> for num in gobj:
    ...     print(num, end=' ')
    ...
    5 6 7 8 9 10
    >>> for num in gobj:
    ...     print(num, end=' ')
    ...
    5 6 7 8 9 10
    

    生成器表达式

    在本节中,我们将学习生成器表达式,它是列表推导和生成器的高性能,内存有效概括。

    例如,我们将尝试将所有数字的平方从1加到9。

    >>> sum([x*x for x in range(1,10)])
    

    该示例实际上首先在内存中创建一个方形值列表,然后迭代它,最后在求和后释放内存。如果列出大的列表,您可以了解内存使用情况。

    我们可以使用生成器表达式来节省内存使用量。

    sum(x*x for x in range(1,10))
    

    生成器表达式的语法表示总是需要直接在一组括号内,并且两边都不能有逗号。这基本上意味着以下两个示例都是有效的生成器表达式用法示例

    >>> sum(x*x for x in range(1,10))
    285
    >>> g = (x*x for x in range(1,10))
    >>> g
    <generator object <genexpr> at 0x7fc559516b90>
    

    我们可以链接生成器或生成器表达式。在下面的示例中,我们将读取文件* / var / log / cron *并查找是否有任何特定作业(在我们搜索anacron的示例中)是否成功运行。

    我们可以使用shell命令tail -f / var / log / cron | grep anacron来做同样的事情

    >>> jobtext = 'anacron'
    >>> all_lines = (line for line in open('/var/log/cron', 'r') )
    >>> job = ( line for line in all_lines if line.find(jobtext) != -1)
    >>> text = next(job)
    >>> text
    "May  6 12:17:15 dhcp193-104 anacron[23052]: Job `cron.daily' terminated\n"
    >>> text = next(job)
    >>> text
    'May  6 12:17:15 dhcp193-104 anacron[23052]: Normal exit (1 job run)\n'
    >>> text = next(job)
    >>> text
    'May  6 13:01:01 dhcp193-104 run-parts(/etc/cron.hourly)[25907]: starting 0anacron\n'
    

    你可以在行上写一个for循环。

    闭包

    闭包只不过是另一个函数返回的函数。我们使用闭包来删除代码重复。在以下示例中,我们创建了一个用于添加数字的简单闭包。

    >>> def add_number(num):
    ...     def adder(number):
    ...         'adder is a closure'
    ...         return num + number
    ...     return adder
    ...
    >>> a_10 = add_number(10)
    >>> a_10(21)
    31
    >>> a_10(34)
    44
    >>> a_5 = add_number(5)
    >>> a_5(3)
    8
    

    adder是一个闭包,它将给定的数字添加到预定义的数字中。

    装饰者

    Decorator是为某些对象动态添加一些新行为的方法。我们通过使用闭包在Python中实现了相同的功能。

    在这个例子中,我们将创建一个简单的例子,它将在执行函数之前和之后打印一些语句。

    >>> def my_decorator(func):
    ...     def wrapper(*args, **kwargs):
    ...         print("Before call")
    ...         result = func(*args, **kwargs)
    ...         print("After call")
    ...         return result
    ...     return wrapper
    ...
    >>> @my_decorator
    ... def add(a, b):
    ...     "Our add function"
    ...     return a + b
    ...
    >>> add(1, 3)
    Before call
    After call
    4
    
    image

    欢迎大家关注公众号:「Python精选」,关注公众号,回复「1024」你懂得,免费领取 6 本经典Python编程书籍。关注我,与 10 万程序员一起进步。每天更新Python干货哦,期待你的到来!

    相关文章

      网友评论

        本文标题:Python迭代器,生成器和装饰器

        本文链接:https://www.haomeiwen.com/subject/anigyqtx.html