为Python 2.4即兴创建“with”语句的替代品

7 投票
5 回答
2011 浏览
提问于 2025-04-15 14:58

你能建议一种方法来编写一个可以替代“with”语句的代码吗?这个替代方案要能在Python 2.4中使用。

虽然这可能有点绕,但这样我就能更顺利地把我的项目迁移到Python 2.4了。

编辑:去掉了不相关的元类草图。

5 个回答

1

因为你需要在出现错误和没有错误的情况下都能退出上下文管理器,所以我觉得用元类来做一个通用的解决方案是不太可能的,实际上也不太可能。你需要使用try/finally结构来处理这个问题。

不过,也许在你的情况下可以做一些其他的事情。这要看你使用上下文管理器的目的是什么。

使用__del__在某些情况下是有帮助的,比如释放资源,但因为你不能保证它一定会被调用,所以它只能用在你需要释放那些在程序退出时会被释放的资源的情况。如果你在__exit__方法中处理异常,这种方法也不适用。

我觉得最简单的方法是把整个上下文管理的过程包裹在一个上下文管理的调用中,然后把代码块提取到一个方法里。像这样(这段代码未经测试,但大部分是从PEP 343中借来的):

def call_as_context_manager(mgr, function):
    exit = mgr.__exit__
    value = mgr.__enter__()
    exc = True
    try:
        try:
            function(value)
        except:
            exc = False
            if not exit(*sys.exc_info()):
                raise
    finally:
        if exc:
            exit(None, None, None)
4

你可以用装饰器来实现这个功能,我觉得这样做是可行的。下面的代码可以正常工作,比如:

def execute_with_context_manager(man):
    def decorator(f):
        target = man.__enter__()
        exc = True
        try:
            try:
                f(target)
            except:
                exc = False
                if not man.__exit__(*sys.exc_info()):
                    raise
        finally:
            if exc:
                man.__exit__(None, None, None)
        return None
    return decorator

@execute_with_context_manager(open("/etc/motd"))
def inside(motd_file):
    for line in motd_file:
        print line,

(不过在Python 2.4中,文件对象没有__enter__和__exit__方法,但其他地方是可以的)

这里的想法是,你用装饰过的函数“声明”来替代下面这行代码:

with bar() as foo:
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

而不是直接使用这行代码,实际上是在:

@execute_with_context_manager( bar() )
def dummyname( foo ):
    do_something_with(foo)
    do_something_else_with(foo)
    # etc...

这样做的效果是一样的(也就是执行do_something_...的代码)。需要注意的是,装饰器把函数声明变成了一个立即调用,这其实有点不太正经。

7

只需要用try-finally就可以了。

其实,这个想法可能在脑子里想想还不错,但如果你真的在你关心的代码里这么做,最后会得到一堆又丑又难维护的代码。

撰写回答