在 if ... else 语句之后保持上下文管理器打开

Keep context manager open after an if ... else statement

我有以下方案,具有不同的条件 with 语句:

if not remote:
    _open = open
    os.chdir(localpath)
else:
    sftp = pysftp.Connection(host, username=user, password=sftppwd)
    with sftp:
        sftp.chdir(remotepath)
        _open = sftp.open

with _open('myfile', 'rb') as f:        # and then lots of other files are opened too
    x = f.read(4)
    ...

最后一行在 remote 情况下失败,因为 sftp 对象/上下文管理器已过期。

我已阅读 ,但这里并不完全相同:我可以为非远程情况创建一个虚拟上下文管理器,但我不确定这是否足够。

我考虑过使用 ExitStack 但我担心打开更多文件时看起来会很复杂:每个简单的 with _open(...) as f: 都需要重写为可读性较差的 stack.enter_context(_open(...)) 当更多代码到达时。

在这种情况下最简单的解决方案是什么?(尽可能避免创建新函数并保持简单的 if ... else 流程)

代码的主要特点是您希望 with _open(): ... 上下文管理器位于 remote 分支的另一个上下文管理器中,并在其他情况。 我认为最简单的方法是使用虚拟上下文管理器,这里有更深入的讨论:

简而言之,您可以在非远程情况下设置虚拟上下文管理器,例如:

import os
# from contextlib import suppress as nullcontext  # Python 3.4+
from contextlib import nullcontext  # Python 3.7+
import pysftp


remote = False
localpath = '.'
remotepath = '.'

if not remote:
    _open_cm = nullcontext()
    _open_cm.chdir = os.chdir
    _open_cm.open = open
    _path = localpath
else:
    _open_cm = pysftp.Connection(host, username=user, password=sftppwd)
    _path = remotepath

with _open_cm:
    _open_cm.chdir(_path)
    with _open_cm.open('myfile', 'rb') as f:
        x = f.read(4)
        ...

(警告!我没有测试过上面的代码,它使用了 monkey-patching,但只是为了让你明白)