我有一个处理一些数据的脚本,如果存在数据库/文件,则将一些数据写入其中。我将数据库或文件指定为 configargparse
(argparse
) 参数。我需要以某种有组织的方式清理(关闭文件、数据库),以防发生异常。
这是我的初始化:
import sqlite3
import confargparse
import sys
parser.ArgParser(...)
parser.add('--database', dest='database',
help='path to database with grabbers', metavar='FILE',
type=lambda x: arghelper.is_valid_file(parser, x))
parser.add('-f', '--file', type=configargparse.FileType(mode='r'))
args = parser.parse_args()
我使用if
和try
做到了:
if args.database:
conn = sqlite3.connect(args.database)
c = conn.cursor()
# same init for file
try:
while True: # do something, it might be moved to some main() function
result = foo()
if args.database:
c.execute('Write to database {}'.format(result))
# same
# for file
finally:
if args.database:
conn.close()
# same
# for file
except KeyboardInterrupt:
print 'keyboard interrupt'
可以用with
语句来完成吗?类似于(这里来自 C 的 ()?():()
):
with ((args.database)?
(conn = sqlite3.connect(args.database)):
(None)) as db, same for file:
然后引用with
子句中的数据库并检查它们是否存在?
最佳答案
先回答你的问题。使用contextlib
可以做到这一点。但我不确定您会从中获得多少 yield 。
from contextlib import contextmanager
@contextmanager
def uncertain_conn(args):
yield sqlite3.connect(args.database) if args.database else None
# Then you use it like this
with uncertain_conn(args) as conn:
# conn will be the value yielded by uncertain_conn(args)
if conn is not None:
try:
# ...
但正如我所说,虽然将生成器函数转变为上下文管理器很酷,而且我个人非常喜欢 contextmanager
装饰器,它确实为您提供了您想要的功能,但我不喜欢不知道它是否真的对你有那么大的帮助。如果我是你,我可能会对 if
感到满意:
if args.database:
conn = sqlite3.connect(args.database)
try:
# ...
不过,有一些事情可以用 with
来简化。查看同样来自 contextlib
的 opening
(非常简单,我只引用文档):
contextlib.closing(thing)
Return a context manager that closes thing upon completion of the block. This is basically equivalent to:
from contextlib import contextmanager @contextmanager def closing(thing): try: yield thing finally: thing.close()
所以上面的代码可以变成:
if args.database:
conn = sqlite3.connect(args.database)
with closing(conn):
# do something; conn.close() will be called no matter what
但这不会为 KeyboardInterrupt
打印一条漂亮的消息。如果你真的需要它,那么我想你仍然需要自己写出 try- except-finally
。做任何更异想天开的事情可能是不值得的。 (请注意,except
必须位于 finally
之前,否则会出现语法错误。)
您甚至可以使用 suppress
来做到这一点(但需要谨慎一些;见下文)
from contextlib import suppress
with suppress(TypeError):
conn = sqlite3.connect(args.database or None)
with closing(conn):
# do business
with override(error): do_thing
相当于
try:
do_thing
except error:
pass
因此,如果 args.database
的计算结果为 False
,则第二行实际上是 connect(None)
,这会引发 TypeError
,它将被上下文管理器捕获,并且下面的代码将被跳过。但风险在于它会抑制其范围内的所有 TypeError
,而您可能不希望这样。
关于Python - 带可选对象的with语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35480918/