使用with语句处理自己的类时需要注意什么?

8
我计划使用便捷的with语句,在我的Python类中实现类似C++构造函数/析构函数的功能。我之前只用过这个语句来进行文件IO,但我认为它对于连接型通信任务也会很有帮助,例如sockets数据库连接等,最终需要关闭的事物。
在PEP 343中(如上所述),提到了with需要__enter____exit__方法,在我的简单实现中,这些方法看起来能够按预期工作。
class MyConnection:
  def __init__(self):
    pass
  def __enter__(self):
    print "constructor"
    # TODO: open connections and stuff
    # make the connection available in the with-block
    return self 
  def __exit__(self, *args):
    print "destructor"
    # TODO: close connections and stuff

with MyConnection() as c:
  # TODO: do something with c
  pass

产生了预期的输出:
constructor
destructor

这么简单就可以了吗?除此之外还有什么需要考虑的因素吗?为什么很多库(显然)缺乏这个功能?我是否漏掉了什么?


那么你将如何在 exit() 中传递参数? - Arseny
__exit__ 接受三个参数:异常类型、其值和回溯(如果没有异常发生,则为 3 个 None)。Python 将它们作为位置参数传递,因此不需要使用 **kwargs - yak
阅读PEP,它会解释所有细节。 - Jochen Ritzel
@yak:没错,已经修复了。 - moooeeeep
@Arseny: 我会避免自己传递参数给__exit__()的需要。 - moooeeeep
@JochenRitzel:我旨在获得实际经验,了解使用这种方法时可能会遇到哪些问题。也许这就是为什么它不是各种任务的常见功能的原因。 - moooeeeep
2个回答

5

(a) 这很容易。

(b) 另一种方法是装饰器函数,它可以装饰函数(和类,但不适用于此用例),并允许在包装的函数之前和之后调用代码。这似乎更常见。

(c) 我认为你没有错过什么。


上下文管理器还允许在执行代码之前(__enter__)和之后(__exit__)添加代码。 - yak
1
@yak:我的回答哪一部分让你认为我不知道那个? - Marcin

0

在尝试在我的库中实现'with'功能时,我遇到的一个问题是找到一种优雅的处理失败异常的方式。 给定以下内容:

class open_file_for_read(object):
    def __init__(self):
        self.filename = "./does_not_exist.txt"
        self.fileobj = None

    def __enter__(self):
        print("Opening file %s for read" % self.filename)
        self.fileobj = open(name=self.filename, mode='r')
        return self.fileobj

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


with open_file_for_read() as fh:
    for li in fh.readlines():
        print(li)

如何处理不可避免的“IOError: [Errno 2] No such file or directory: './does_not_exist.txt'”异常?通常采用的方法是使用'try/except'语句块。

try:
    with open_file_for_read() as fh:
except IOError:
    ...
except XXX:
    ...
...

这种直接的方法虽然可行,但我认为它会削弱使用“with”结构的简洁性。也许有人有更优雅的解决方案呢?

很抱歉这更像是一个问题而不是答案,但这是我在尝试实现“with”时遇到的问题之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接