【问题标题】:Use with statement in a class that wraps a resource在包装资源的类中使用 with 语句
【发布时间】:2013-08-07 19:30:27
【问题描述】:

如果我有一个包装资源的类,例如sqlite 数据库连接或文件,有没有办法可以在我的对象超出范围时使用with 语句关闭资源或收集到了吗?

为了澄清我的意思,我想避免这种情况:

class x:
    def __init__(self):
        # open resource

    def close(self): # or __del__, even worst
        # close resource

但要确保资源总是像下面那样被释放

with open('foo') as f:
    # use resource

【问题讨论】:

    标签: python resources with-statement


    【解决方案1】:

    您需要提供__enter__ 和__exit__ 方法。见PEP 343。

    此 PEP 为 Python 语言添加了一个新语句“with”以使其 可以排除 try/finally 语句的标准用法。

    在此 PEP 中,上下文管理器提供 __enter__() 和 __exit__() 在进入和退出主体时调用的方法 with 声明。

    【讨论】:

    • 如果您利用contextlib,您实际上不需要这样做。 contextlib 会为您处理。
    • @user2357112 如果您使用的是不提供上下文管理器的外部 API,则该选项是合适的。如果你是编写一个,你应该把它放到API中,而不是强迫调用者处理清理。
    • 啊,这是关于编写 API,而不是使用它。那么,自己提供方法是有意义的。
    【解决方案2】:

    使用contextlib.closing:

    with contextlib.closing(thing) as thing:
        do_stuff_with(thing)
    # Thing is closed now.
    

    【讨论】:

    • 我认为这不能回答他正在寻找的问题。他希望thing 成为x 实例的属性,并在x 被释放时调用thing.close()。所以,没有静态范围,他可以在其中放入 with 语句。
    • @abarnert:不,我很确定他想使用with 声明。这个问题甚至说“有没有办法可以使用with 语句”。在 Python 中依靠对象销毁来关闭资源是一个坏主意。 (由于他正在编写 API 而不是处理不提供上下文管理器方法的 API,因此 roippi 的答案更合适,所以我赞成那个。)
    • 是的,他想知道是否有办法在他的班级中使用with 语句。由于他的对象的生命周期不受任何静态范围的约束,答案是否定的,他不能那样做。这是一个合理的问题——在某些语言中,比如 C++,你不仅可以,而且通常应该让对象管理它自己的资源。但是在 Python 中,你不能也不应该;对象的用户需要明确地close它;对象唯一能做的就是让他们更容易。
    • @abarnert:我认为我们对这个问题的解释不同。我看到关于“使用with 语句”的部分和带有opened 文件的示例代码,并认为他想做with x() as thing: do_stuff_with(thing)。我猜你看到了关于“当我的对象超出范围或被 gcollected”的部分,并认为他正在寻找一个析构函数。我认为那部分只是对with 声明的轻微误解。我很确定他希望能够在with 语句中使用他的对象,而不是在他的对象中使用with 语句。
    • OP 甚至说__del__ 方法比close 方法“更糟糕[原文如此]”,他已经在努力避免这种情况。
    【解决方案3】:

    您可以始终将所需的任何清理代码放入类的__del__ 方法中:

    class x:
        def __init__(self):
            self.thing = get_thing()
    
        def __del__(self):
            self.thing.close()
    

    但你不应该。

    这是一个坏主意,有几个原因。如果您使用的是 CPython,拥有自定义 __del__ 方法意味着 GC 不能中断引用循环。如果您使用大多数其他 Python 实现,__del__ 方法不会在可预测的时间被调用。

    这就是为什么您通常将清理放在显式的close 方法中。这是你在课堂上能做的最好的事情。确保调用 close 方法而不是类本身始终取决于类的用户。

    因此,您无法在课堂内使用with 语句或任何等效语句。但是你可以让你的班级的用户更容易使用with 语句,方法是让你的班级成为上下文管理器,如 roippi 的回答中所述,或者只是建议他们在你的文档中使用contextlib.closing。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-23
      • 1970-01-01
      • 1970-01-01
      • 2022-08-15
      • 1970-01-01
      • 2020-12-29
      • 2017-05-03
      • 1970-01-01
      相关资源
      最近更新 更多