如何提前退出 __enter__ 并跳过 Python 中的 with 块?

How can I prematurely exit __enter__ and skip the with block in Python?

如果 __enter__ 函数中存在错误,我一直在尝试找到一种方法来完全 exit/skip 我的 with BaseObject.ethernet_device(ip_addr) as obj: 代码块。

在下面的示例中,如果该设备不在网络中,则会出现这种情况。任何解决方案都有望取代 return None 行(我知道这不是必需的)。

class BaseObject(object):
    @class_method
    def ethernet_device(cls, host:str):
        return EthernetDevice(host)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self._inst.close()
class EthernetDevice(BaseObject):
    def __init__(self, host: str):
        self._host = host

    def __enter__(self):
        try:
            socket.inet_ntoa(self._host)
            ip_addr = self._host
        except:
            logger.debug(f'{self._host} is not a valid IP address: trying as a hostname')
            try:
                logger.debug(f"Trying to resolve host {self._host}")
                ip_addr = socket.gethostbyname(self._host)
            except socket.gaierror:
                logger.error(f"Couldn't resolve host {self._host}")
                return None

        self._inst = vxi11.Instrument(ip_addr)

        return super().__enter__()

用法:

with BaseObject.ethernet_device(ip_addr) as device:
   print(device.id)

这里唯一的答案是在 __enter__ 中引发异常;如果 __enter__ 无异常退出,__exit__ 将始终 运行(假设您没有设法对解释器进行段错误或调用 os._exit 故意终止程序而没有 运行宁清理块)。

这将使调用者捕获异常(用 try/except 包装 with)以防止它在程序的其余部分冒泡。

如果调用者不喜欢为此缩进整个块,他们可以使用 an ExitStack 缩小范围,但这是唯一可用的“改进”,它仍然需要 __enter__引发异常:

with contextlib.ExitStack() as stack:
    try:
        device = stack.enter_context(BaseObject.ethernet_device(ip_addr)
    except CustomException:
        return  # Or do something else to skip remaining code
    # Rest of code using device

# device is cleaned up for you if it exists