Python 上下文管理器是类还是函数?

Python 上下文管理器是类还是函数?,python,function,class,contextmanager,Python,Function,Class,Contextmanager,最近,我一直在研究Python的contextmanager(更具体地说,是Python 3的contextlib或其后端口contextlib2),我想知道将它们作为类与函数编写有哪些优点/缺点 它们似乎都以相同的方式运行,并以相同的方式处理异常。有很多很酷的实用程序,比如ExitStack(),但这些实用程序似乎可以在作为类或函数编写的上下文管理器中实现。因此,我正在努力寻找一个很好的理由来解释为什么当上下文管理器可以作为一个函数编写并且只需使用contextmanager装饰器时,人们会希

最近,我一直在研究Python的contextmanager(更具体地说,是Python 3的contextlib或其后端口contextlib2),我想知道将它们作为类与函数编写有哪些优点/缺点

它们似乎都以相同的方式运行,并以相同的方式处理异常。有很多很酷的实用程序,比如ExitStack(),但这些实用程序似乎可以在作为类或函数编写的上下文管理器中实现。因此,我正在努力寻找一个很好的理由来解释为什么当上下文管理器可以作为一个函数编写并且只需使用contextmanager装饰器时,人们会希望将其作为一个类来详细编写

下面是我写的一个小例子,展示了两者做同样的事情:

# !/usr/bin/python -u
# encoding: utf-8

from contextlib import contextmanager

# Function-based
@contextmanager
def func_custom_open(filename, mode):
    try:
        f = open(filename, mode)
        yield f
    except Exception as e:
        print(e)
    finally:
        f.close()

# Class-based
class class_custom_open(object):

    def __init__(self, filename, mode):
        self.f = open(filename, mode)

    def __enter__(self):
        return self.f

    def __exit__(self, type, value, traceback):
        self.f.close()


if __name__ == '__main__':

    # Function-based
    with func_custom_open('holafile_func.txt', 'w') as func_f:
        func_f.write('hola func!')

    # Class-based
    with class_custom_open('holafile_class.txt', 'w') as class_f:
        class_f.write('hola class!')
如果您不需要使用语法的“verbose”类,那么您也不需要它,就这么简单

两者都存在的原因是使用类的方式是上下文管理器在该语言中工作的实际方式。任何在其类中具有
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
方法的对象都可以用作上下文管理器

使用
@contextmanager
并允许将上下文管理器声明为函数的方法只是Python标准库中的一个实用工具。装饰器产生的是一个同时具有两种方法的对象


将上下文管理器编写为类可能更紧凑的一种情况是,用作上下文管理器的对象也是您控制的类,然后您可以更好地在其生命周期中集成运行
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu。例如,经常会看到可以用作装饰器或上下文管理器的对象(
unittest.mock.patch
)。对用户来说,这听起来很“神奇”,但实现非常清晰且定义良好:在这样一个对象的类中,它作为上下文管理器的逻辑是打开
\uuuuuuuuuuuuuuuuuuuuu
/
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu,实现decorator行为的逻辑在
\uuuu call\uuuu
方法上。

如果希望上下文管理器是上下文管理器之外的任何东西,则不能使用
@contextmanager