我怎样才能从函数中产生通知和 return 结果? (Python)
How can I yield notifications and return a result from a function? (Python)
我必须创建一个在内部调用中做一些艰苦工作的函数。这个函数需要是一个生成器,因为我正在使用服务器发送的事件。因此,我希望此函数通过使用“收益”来通知计算进度。之后,这个函数必须将结果传递给父函数才能继续进行其他计算。
我想要这样的东西:
def hardWork():
for i in range(N):
# hard work
yield 'Work done: ' + str(i)
# Here is the problem: I can't return a result if I use a yield
return result
def generator():
# do some calculations
result = hardWork()
# do other calculations with this result
yield finalResult
我找到了一个解决方案,它包含一个 yields 字典,告诉该函数是否已完成,但执行此操作的代码非常脏。
还有其他解决方案吗?
谢谢!
编辑
我想是这样的:
def innerFunction(gen):
calc = 1
for iteration in range(10):
for i in range(50000):
calc *= random.randint(0, 10)
gen.send(iteration)
yield calc
def calcFunction(gen):
gen2 = innerFunction(gen)
r = next(gen2)
gen.send("END: " + str(r + 1))
gen.send(None)
def notifier():
while True:
x = yield
if x is None:
return
yield "Iteration " + x
def generator():
noti = notifier()
calcFunction(noti)
yield from noti
for g in generator():
print(g)
但是我收到这个错误:
TypeError: can't send non-None value to a just-started generator
在 Python3.5 之前:生成器
此解决方案也适用于更新的 Python 版本,尽管 Python3.5 中的新版本 async def
似乎更适合您的用例。 见下一节。
生成器产生的值是通过迭代或使用next
获得的。最后返回的值存储在指示生成器结束的 StopIteration
异常的 value
属性中。还好恢复的不是太难
def hardWork():
output = []
for i in range(10):
# hard work
yield 'Doing ' + str(i)
output.append(i ** 2)
return output
def generator():
# do some calculations
work = hardWork()
while True:
try:
print(next(work))
except StopIteration as e:
result = e.value
break
yield result
例子
foo = generator()
print(next(foo))
输出
Doing 0
Doing 1
Doing 2
Doing 3
Doing 4
Doing 5
Doing 6
Doing 7
Doing 8
Doing 9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Python3.5+: async def
如果您是 运行 Python3.5+,您正在尝试的似乎非常适合使用可等待函数的事件循环。
import asyncio
async def hardWork():
output = []
for i in range(10):
# do hard work
print('Doing ', i)
output.append(i**2)
# Break point to allow the event loop to do other stuff on the side
await asyncio.sleep(0)
return output
async def main():
result = await asyncio.wait_for(hardWork(), timeout=None)
print(result)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
输出
Doing 0
Doing 1
Doing 2
Doing 3
Doing 4
Doing 5
Doing 6
Doing 7
Doing 8
Doing 9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
我的建议是将您的功能嵌入 class。
def Worker:
def hardWork(self):
self.Finished = False
for i in range(10):
yield 'Work done: ' + str(i)
self.Finished = True
self.Result = 'result'
def generator(self):
while (not self.Finished):
print(next(self.hardWork()))
return self.Result
这将具有您想要的功能,而不必担心围绕异常抛出-捕获逻辑对逻辑进行编程。
Python 3.3 引入了 yield from
for generator delegation,这几乎正是您所要求的。 yield from
允许主生成器将功能委托给另一个函数,该函数也包含 yield
语句,如下所示:
def hardWork():
for i in range(N):
# hard work
yield 'Work done so far: ' + str(i)
# With this construction, you can still return a result after the `yield`s
return result
def generator():
# here's the new construction that makes it all work:
result = yield from hardWork()
# do other calculations with this result
yield finalResult
我必须创建一个在内部调用中做一些艰苦工作的函数。这个函数需要是一个生成器,因为我正在使用服务器发送的事件。因此,我希望此函数通过使用“收益”来通知计算进度。之后,这个函数必须将结果传递给父函数才能继续进行其他计算。
我想要这样的东西:
def hardWork():
for i in range(N):
# hard work
yield 'Work done: ' + str(i)
# Here is the problem: I can't return a result if I use a yield
return result
def generator():
# do some calculations
result = hardWork()
# do other calculations with this result
yield finalResult
我找到了一个解决方案,它包含一个 yields 字典,告诉该函数是否已完成,但执行此操作的代码非常脏。
还有其他解决方案吗?
谢谢!
编辑
我想是这样的:
def innerFunction(gen):
calc = 1
for iteration in range(10):
for i in range(50000):
calc *= random.randint(0, 10)
gen.send(iteration)
yield calc
def calcFunction(gen):
gen2 = innerFunction(gen)
r = next(gen2)
gen.send("END: " + str(r + 1))
gen.send(None)
def notifier():
while True:
x = yield
if x is None:
return
yield "Iteration " + x
def generator():
noti = notifier()
calcFunction(noti)
yield from noti
for g in generator():
print(g)
但是我收到这个错误:
TypeError: can't send non-None value to a just-started generator
在 Python3.5 之前:生成器
此解决方案也适用于更新的 Python 版本,尽管 Python3.5 中的新版本 async def
似乎更适合您的用例。 见下一节。
生成器产生的值是通过迭代或使用next
获得的。最后返回的值存储在指示生成器结束的 StopIteration
异常的 value
属性中。还好恢复的不是太难
def hardWork():
output = []
for i in range(10):
# hard work
yield 'Doing ' + str(i)
output.append(i ** 2)
return output
def generator():
# do some calculations
work = hardWork()
while True:
try:
print(next(work))
except StopIteration as e:
result = e.value
break
yield result
例子
foo = generator()
print(next(foo))
输出
Doing 0
Doing 1
Doing 2
Doing 3
Doing 4
Doing 5
Doing 6
Doing 7
Doing 8
Doing 9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Python3.5+: async def
如果您是 运行 Python3.5+,您正在尝试的似乎非常适合使用可等待函数的事件循环。
import asyncio
async def hardWork():
output = []
for i in range(10):
# do hard work
print('Doing ', i)
output.append(i**2)
# Break point to allow the event loop to do other stuff on the side
await asyncio.sleep(0)
return output
async def main():
result = await asyncio.wait_for(hardWork(), timeout=None)
print(result)
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
输出
Doing 0
Doing 1
Doing 2
Doing 3
Doing 4
Doing 5
Doing 6
Doing 7
Doing 8
Doing 9
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
我的建议是将您的功能嵌入 class。
def Worker:
def hardWork(self):
self.Finished = False
for i in range(10):
yield 'Work done: ' + str(i)
self.Finished = True
self.Result = 'result'
def generator(self):
while (not self.Finished):
print(next(self.hardWork()))
return self.Result
这将具有您想要的功能,而不必担心围绕异常抛出-捕获逻辑对逻辑进行编程。
Python 3.3 引入了 yield from
for generator delegation,这几乎正是您所要求的。 yield from
允许主生成器将功能委托给另一个函数,该函数也包含 yield
语句,如下所示:
def hardWork():
for i in range(N):
# hard work
yield 'Work done so far: ' + str(i)
# With this construction, you can still return a result after the `yield`s
return result
def generator():
# here's the new construction that makes it all work:
result = yield from hardWork()
# do other calculations with this result
yield finalResult