Python - 带可选对象的with语句

标签 python exception try-catch with-statement

我有一个处理一些数据的脚本,如果存在数据库/文件,则将一些数据写入其中。我将数据库或文件指定为 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()

我使用iftry做到了:

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/

相关文章:

python - 如何在 Python 中按姓氏对从 txt 文件导入的名称列表进行排序

python - django:将每个用户的数据分开

python - 深度学习和神经网络

.net - CLR 异常系统中的故障子句是什么?

java - 在包含 return 的 try/catch block 之后执行语句

mysql - 如何在 try catch block 中捕获 MySQL 错误?

python - 使用python检查网页中是否出现两次单词

java - 在java中使用反射时出现未处理的异常

python - 一个简单的 GUI 应用程序中有多少个线程?

java - 在 try block 中初始化一个变量,一种解决方法?