你为什么不使用contextmanager?它基本上完全符合您的要求。
这是 Python 文档中的规范示例。
from contextlib import contextmanager
@contextmanager
def tag(name):
print "<%s>" % name
yield
print "</%s>" % name
因此,对于您的功能,您只需执行以下操作:
@contextmanager
def profile_update(inputs):
#take updates and update the database
yield "it worked"
#do maintainence processing now..
要调用它,您只需执行以下操作:
with profile_update(inputs) as result: #pre-yield and yield here
# do whatever while in scope
# as you move out of scope of with statement, post-yield is executed
编辑:我只是在测试,结果发现,使用 yield 语句,函数仍然执行到最后。这是一个愚蠢的例子,说明了要点和事情何时执行。
def some_generator(lst):
for elem in lst:
yield elem
lst[0] = "I WAS CHANGED POST-YIELD!!!!"
>>> q = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> gen = some_generator(q)
>>> for e in gen:
... print e, q
0 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
3 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
5 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
6 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
7 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
8 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
9 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print q
['I WAS CHANGED POST YIELD!!!', 1, 2, 3, 4, 5, 6, 7, 8, 9]
contextmanager 的优点是不需要两次next
调用即可到达停止迭代(和更简洁的语法),但是如果您想返回多个值或其他内容,您也可以这样做,但是您可以看到 post yield 语句直到生成器在调用中引发 StopIteration 才真正被next
调用(for 循环在它 get 时结束StopIteration
)
如果由于某种原因,您需要比 offer 更高程度的控制@contextmanager
,您还可以使用__enter__
和__exit__
方法定义一个类:
class MyContextClass(object):
# ...
def __enter__(self):
# do some preprocessing
return some_object
def __exit__(self, exc_type, exc_value, traceback):
# do some post processing
# possibly do some processing of exceptions raised within the block
if exc_type == MyCustomErrorType:
return True #don't propagate the error