在以下情况下使用生成器函数有什么好处?
What are the advantages of using a generator function in the following case?
我的作业objective是无限期地生成列表元素。
所以我这样做了:
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
那么生成器函数有什么好处呢?
使用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()
.
之间增加计数器
在此上下文中,列表是它自己的生成器:
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
但是不要这样做,它的效率较低并且需要更多代码 reader 的内存。
我的作业objective是无限期地生成列表元素。 所以我这样做了:
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
那么生成器函数有什么好处呢?
使用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()
.
在此上下文中,列表是它自己的生成器:
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
但是不要这样做,它的效率较低并且需要更多代码 reader 的内存。