我正在尝试编写一个生成器函数,该函数从数据库中获取行并一次返回一行。但是,我不确定下面标记为 ** 的清理代码是否按照我的想法执行。如果没有,将清理代码放入生成器本身并在最后一个yield语句之后执行的最佳方法是什么?我查看了捕获 StopIteration 但这似乎是由调用者完成的,而不是在生成器内完成的。
def MYSQLSelectGenerator(stmt):
...
try:
myDB = MySQLdb.connect(host=..., port=..., user=..., passwd=..., db=...)
dbc=myDB.cursor()
dbc.execute(stmt)
d = "asdf"
while d is not None:
d = dbc.fetchone() #can also use fetchmany() to be more efficient
yield d
dbc.close() #** DOES THIS WORK AS I INTEND, MEANING AS SOON AS d = "None"
except MySQLdb.Error, msg:
print("MYSQL ERROR!")
print msg
您的版本将在
dbc.close()
后立即运行 d is None
,但 如果引发异常则不会运行。您需要一个 finally
子句。即使引发异常,此版本也保证运行dbc.close()
:
try:
myDB = MySQLdb.connect(host=..., port=..., user=..., passwd=..., db=...)
dbc = myDB.cursor()
dbc.execute(stmt)
d = "asdf"
while d is not None:
d = dbc.fetchone() #can also use fetchmany() to be more efficient
yield d
except MySQLdb.Error, msg:
print("MYSQL ERROR!")
print msg
finally:
dbc.close()
您可以做的一件事是使用
finally
子句。另一种选择(这里可能有点过分,但了解一下很有用)是创建一个与 with
语句一起使用的类:
class DatabaseConnection:
def __init__(self, statement):
self.statemet = statement
def __enter__(self):
self.myDB = MySQLdb.connect(host=..., port=...,user=...,passwd=...,db=...)
self.dbc = myDB.cursor()
self.dbc.execute(self.statement)
self.d = "asdf"
def __exit__(self, exc_type, exc_value, traceback):
self.dbc.close()
def __iter__(self):
while self.d is not None:
self.d = self.dbc.fetchone()
yield self.d
with DatabaseConnection(stmnt) as dbconnection:
for i in dbconnection:
print(i)
您可以使用上下文管理器和
with
语句。 contextlib
提供closing
:
from contextlib import closing
myDB = MySQLdb.connect(host=..., port=..., user=..., passwd=..., db=...)
with closing(myDB.cursor()) as dbc:
dbc.execute(stmt)
d = "asdf"
while d is not None:
d = dbc.fetchone() #can also use fetchmany() to be more efficient
yield d
即使已引发异常,这也会在
close()
块末尾自动调用 dbc
上的 with
。