Python - with 带有可选对象的语句
Python - with statement with optional object
我有一个处理一些数据的脚本,如果 database/file 存在,则将一些数据写入其中。我将数据库或文件指定为 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
子句中引用数据库并检查它们是否存在?
在这种情况下,您可以创建自己的上下文管理器。创建一个处理两个连接的。上下文管理器是一个 class,它具有方法 __enter__()
和 __exit__()
。一种在进入 with
子句之前调用,一种在离开时调用(无论如何)。
下面是一个关于如何在您的情况下执行此操作的示例:
def f(cond1, cond2):
class MultiConnectionContextManager(object):
def __init__(self, cond1, cond2):
self.cond1 = cond1
self.cond2 = cond2
def __enter__(self):
print "entering ..."
if self.cond1:
# self.connection1 = open(...)
print "opening connection1"
if self.cond2:
# self.connection1 = open(...)
print "opening connection2"
return self
def __exit__(self, exc_type, exc_value, traceback):
print "exiting ..."
if self.cond1:
# self.connection1.close()
print "closing connection1"
if self.cond2:
# self.connection2.close()
print "closing connection2"
with MultiConnectionContextManager(cond1, cond2) as handle:
if cond1:
# handle.connection1.read()
print "using handle.connection1"
if cond2:
# handle.connection2.read()
print "using handle.connection2"
for cond1 in (False, True):
for cond2 in (False, True):
print "=====", cond1, cond2
f(cond1, cond2)
你可以直接调用这个看看结果。将 print
替换为您打开、使用和关闭连接的真实语句。
先回答你的问题。可以使用 contextlib
来完成。但我不确定你能从中得到多少。
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
简化的事情。查看 closing
,也来自 contextlib
(非常简单,我只引用文档):
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 suppress(error): do_thing
等价于
try:
do_thing
except error:
pass
因此,如果 args.database
的计算结果为 False
,则第二行实际上是 connect(None)
,它会引发一个 TypeError
,它将被上下文管理器和下面的代码将被跳过。但风险在于它会抑制其范围内的所有 TypeError
,而您可能不希望这样。
我有一个处理一些数据的脚本,如果 database/file 存在,则将一些数据写入其中。我将数据库或文件指定为 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
子句中引用数据库并检查它们是否存在?
在这种情况下,您可以创建自己的上下文管理器。创建一个处理两个连接的。上下文管理器是一个 class,它具有方法 __enter__()
和 __exit__()
。一种在进入 with
子句之前调用,一种在离开时调用(无论如何)。
下面是一个关于如何在您的情况下执行此操作的示例:
def f(cond1, cond2):
class MultiConnectionContextManager(object):
def __init__(self, cond1, cond2):
self.cond1 = cond1
self.cond2 = cond2
def __enter__(self):
print "entering ..."
if self.cond1:
# self.connection1 = open(...)
print "opening connection1"
if self.cond2:
# self.connection1 = open(...)
print "opening connection2"
return self
def __exit__(self, exc_type, exc_value, traceback):
print "exiting ..."
if self.cond1:
# self.connection1.close()
print "closing connection1"
if self.cond2:
# self.connection2.close()
print "closing connection2"
with MultiConnectionContextManager(cond1, cond2) as handle:
if cond1:
# handle.connection1.read()
print "using handle.connection1"
if cond2:
# handle.connection2.read()
print "using handle.connection2"
for cond1 in (False, True):
for cond2 in (False, True):
print "=====", cond1, cond2
f(cond1, cond2)
你可以直接调用这个看看结果。将 print
替换为您打开、使用和关闭连接的真实语句。
先回答你的问题。可以使用 contextlib
来完成。但我不确定你能从中得到多少。
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
简化的事情。查看 closing
,也来自 contextlib
(非常简单,我只引用文档):
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 suppress(error): do_thing
等价于
try:
do_thing
except error:
pass
因此,如果 args.database
的计算结果为 False
,则第二行实际上是 connect(None)
,它会引发一个 TypeError
,它将被上下文管理器和下面的代码将被跳过。但风险在于它会抑制其范围内的所有 TypeError
,而您可能不希望这样。