104

我必须在 python 中打开一个类似文件的对象(它是通过 /dev/ 的串行连接)然后关闭它。这在我班的几种方法中多次完成。我的做法是在构造函数中打开文件,然后在析构函数中关闭它。不过我遇到了一些奇怪的错误,我认为这与垃圾收集器等有关,我仍然不习惯不知道我的对象何时被删除=\

我这样做的原因是因为tcsetattr每次打开它时我都必须使用一堆参数,而且在所有地方都这样做很烦人。所以我想实现一个内部类来处理所有这些,所以我可以用它来做
with Meter('/dev/ttyS2') as m:

我在网上看,我找不到关于如何实现with语法的真正好的答案。我看到它使用__enter__(self)and__exit(self)__方法。但是我所要做的就是实现这些方法并且我可以使用 with 语法吗?或者还有更多?

是否有一个关于如何执行此操作的示例或一些关于它如何在我可以查看的文件对象上实现的文档?

4

3 回答 3

140

这些方法几乎是使对象与with语句一起工作所需的全部。

__enter__打开文件对象并进行设置后,您必须返回它。

__exit__你必须关闭文件对象。写入它的代码将在with语句体中。

class Meter():
    def __init__(self, dev):
        self.dev = dev
    def __enter__(self):
        #ttysetattr etc goes here before opening and returning the file object
        self.fd = open(self.dev, MODE)
        return self
    def __exit__(self, type, value, traceback):
        #Exception handling here
        close(self.fd)

meter = Meter('dev/tty0')
with meter as m:
    #here you work with the file object.
    m.fd.read()
于 2010-09-22T23:31:17.673 回答
51

最简单的可能是使用标准 Python 库模块contextlib

import contextlib

@contextlib.contextmanager
def themeter(name):
    theobj = Meter(name)
    try:
        yield theobj
    finally:
        theobj.close()  # or whatever you need to do at exit


# usage
with themeter('/dev/ttyS2') as m:
    # do what you need with m
    m.read()

这并不使Meter自己成为一个上下文管理器(因此对那个类是非侵入性的),而是“装饰”它(不是在 Python 的“装饰器语法”的意义上,而是在某种意义上几乎,但不完全是装饰器设计模式的;-) 带有一个工厂函数themeter,它一个上下文管理器(contextlib.contextmanager装饰器从您编写的“单一yield”生成器函数构建)——这使得分离进入和退出条件变得更加容易,避免嵌套等。

于 2010-09-23T02:06:38.710 回答
-10

第一个谷歌热门(对我来说)解释得很简单:

http://effbot.org/zone/python-with-statement.htm

PEP 更准确地解释了它(但也更冗长):

http://www.python.org/dev/peps/pep-0343/

于 2010-09-22T23:28:59.780 回答