在下列情况下使用发电机功能有什么好处?

时间:2016-02-06 13:08:13

标签: python python-2.7 generator

我的任务目标是无限期地生成列表元素。 所以我这样做了:

SERVERS = ['APP1', 'APP2', 'APP3']
#SERVERS = ['APP1', 'APP2', 'APP3', 'APP4', 'APP5', 'APP6']
length = len(SERVERS)

def get_server():
    current_server = SERVERS.pop(0)
    SERVERS.append(current_server)
    return current_server

if __name__ == '__main__':
    for i in range(9):
        print get_server()

解决方案有这样的事情:

SERVERS = ['APP1', 'APP2', 'APP3']
#SERVERS = ['APP1', 'APP2', 'APP3', 'APP4', 'APP5', 'APP6']

def get_server():
    def f():
        while True:
            i = SERVERS.pop(0)
            SERVERS.append(i)
            yield i
    return next(f())

if __name__ == '__main__':
    for i in range(9):
        print get_server()

输出虽然在两种情况下都相同:

codewingx@CodeLair:~/repo/python$ python load_balancer.py
APP1
APP2
APP3
APP1
APP2
APP3
APP1
APP2
APP3

那么发电机功能如何有益呢?

2 个答案:

答案 0 :(得分:2)

使用itertools.cycle()

生成器不会在这里添加任何有用的东西。我会尽量避免使用pop(0),因为它每次都会触发整个服务器列表的重建。

我建议itertools.cycle()

from __future__ import print_function

from itertools import cycle

SERVERS = ['APP1', 'APP2', 'APP3']

servers = cycle(SERVERS)

for i in range(9):
    print(next(servers))

输出:

APP1
APP2
APP3
APP1
APP2
APP3
APP1
APP2
APP3

我们的功能与您的用法相匹配:

def make_get_server():
    servers = cycle(SERVERS)
    def get_server():
        return next(servers)
    return get_server

get_server = make_get_server()

for i in range(9):
    print(get_server())

输出:

APP1
APP2
APP3
APP1
APP2
APP3
APP1
APP2
APP3

编写自己的生成器函数

为了说明生成器,利用其存储stet的能力的变体可能更有用:

def gen():
    index = 0
    end = len(SERVERS)
    while True:
        yield SERVERS[index]
        index += 1
        if index >= end:
            index = 0

虽然这很好地说明了您正在使用index状态,但可以通过以下方式更轻松地实现:

def gen():
    while True:
        for server in SERVERS:
            yield server

g = gen()

def get_server():
    return next(g)

这可以避免修改SERVERS列表。结果是一样的:

for i in range(9):
    print(get_server())

输出:

APP1
APP2
APP3
APP1
APP2
APP3
APP1
APP2
APP3

发电机如何工作

一个简单的生成器函数:

>>> def gen():
...     print('start')
...     yield 1
...     print('after 1')
...     yield 2
...     print('after 2')
...

制作一个实例:

>>> g = gen()

使用next获取yield返回的下一个值:

>>> next(g)
start
1

继续前进:

>>> next(g)
after 1
2

现在已经筋疲力尽了:

>>> next(g)
after 2

StopIteration  next(g)

您可能会想到在生成器函数中移动的光标。每当您拨打next()时,它都会移至下一个yield。因此将yield置于while True循环中会产生无限生成器。只要您不在其上调用close(),它就会为您提供yield的新值。此外,您在发电机内部有状态。这意味着您可以执行诸如在next()的调用之间递增计数器之类的事情。

答案 1 :(得分:1)

在此上下文中,列表是它自己的生成器:

for i in SERVERS:
    do_something_with_element(i)

如果你想要一个无限的发电机,@MikeMüller的itertools.cycle是不重新发明轮子的首选。如果你必须自己做:

def my_cycle(s):
    while True:
        for i in s:
             yield i

但不要,效率较低,并要求更多代码阅读器的内存。