首页 > 解决方案 > 如何制作一个也可用作上下文管理器的 python 方法?

问题描述

在我正在构建的图形 API 中,我想创建一个也可以用作上下文管理器的方法:

该方法当前如下所示:

class Gfx:
    def __init__(self):
        self._fill = None
    
    def fill(self, color):
        self._fill = color

我可以制作一个在使用后恢复状态的上下文管理器,如下所示:

class Gfx:
    def __init__(self):
        self._fill = None

    @contextmanager
    def fill(self, color):
        """Allow user to set fill color, then restore it"""
        old_fill = self._fill
        self._fill = color 
        yield
        self._fill = old_fill

但是我怎样才能使这两种方式都起作用,这取决于它的调用方式?

>>> gfx = Gfx()
>>> gfx.fill("red") # use as ordinary method
>>> print(self._fill)
"red"

>>> with gfx.fill("blue") # use as context manager
...     print(gfx._fill)
"blue"
>>> print(gfx._fill)
"red"

标签: pythonstatewith-statementcontextmanager

解决方案


您需要混合使用这些方法;有一个 this 被调用的函数,返回一个上下文管理器,其中上下文管理器在不用于with. 类似于以下内容:

class Gfx:
    def __init__(self):
        self._fill = None

    @contextmanager
    def _fillctx(self, old_fill):
        try:
            yield
        finally:
            self._fill = old_fill

    def fill(self, color):
        """Allow user to set fill color, then restore it"""
        old_fill = self._fill
        self._fill = color
        return self._fillctx(old_fill)

当不使用 调用时with,它设置self._fill并返回一个从未使用过的上下文管理器。当使用 调用时with,它将在调用该上下文管理器的self._fill时重置。__exit__

需要明确的是,最好只使用单独的方法,因此您的“普通方法”方法更有效,并且您的上下文管理器方法可以更安全一些(实际上__enter__按照您的预期进行设置工作,缩小范围会阻止__exit__被调用的竞争条件的窗口)。


推荐阅读