7

您能否建议一种方法来编写一个可在 Python 2.4 中使用的“with”语句的替代代码?

这将是一个 hack,但它可以让我更好地将我的项目移植到 Python 2.4。

编辑: 删除不相关的元类草图

4

5 回答 5

7

只需使用try-finally。

真的,这可能是一种很好的心理锻炼,但如果你真的在你关心的代码中做这件事,你最终会得到丑陋的、难以维护的代码。

于 2009-10-10T10:47:10.890 回答
4

我认为你可以(ab)使用装饰器来做到这一点。以下作品,例如:

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_... 代码)。请注意,装饰器将函数声明更改为立即调用,这不仅有点邪恶。

于 2009-10-10T10:11:16.290 回答
1

由于您需要在错误期间而不是错误期间退出上下文管理器,因此我认为不可能使用元类执行通用用例,或者实际上根本不可能。为此,您将需要 try/finally 块。

但也许在你的情况下可以做其他事情。这取决于您使用上下文管理器的目的。

using__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)
于 2009-10-10T10:13:20.450 回答
1

这个怎么样?

def improvize_context_manager(*args, **kwargs):

    assert (len(args) + len(kwargs)) == 1
    if args:
        context_manager = args[0]
        as_ = None
    else: # It's in kwargs
        (as_, context_manager) = kwargs.items()[0]

    def decorator(f):
        exit_ = context_manager.__exit__  # Not calling it yet
        enter_ = context_manager.__enter__()
        exc = True
        try:
            try:
                if as_:
                    f(*{as_: enter_})
                else:
                    f()
            except:
                exc = False
                if not exit_(*sys.exc_info()):
                    raise
        finally:
            if exc:
            exit_(None, None, None)
        return None
    return decorator

用法:

@improvize_context_manager(lock)
def null():
    do(stuff)

这与with没有的关键字平行as

或者:

@improvize_context_manager(my_lock=lock)
def null(my_lock):
    do(stuff_with, my_lock)

with关键字与as.

于 2009-10-10T19:46:39.323 回答
1

如果你可以使用 def 来获取一个块,以及立即执行的装饰器,你可以使用函数签名来为命名的案例获得更自然的东西。

导入系统
带(函数)的定义:
    def 装饰(body = func):
        上下文 = body.func_defaults
        尝试:
            exc = 无,无,无
            尝试:
                对于上下文中的上下文:
                    上下文.__enter__()
                身体()
            除了:
                exc = sys.exc_info()
                增加
        最后:
            对于反转的上下文(上下文):
                上下文.__exit__(*exc)
    装饰()

类上下文(对象):
    def __enter__(self):
        打印 "输入 %s" % self
    def __exit__(self, *args):
        print "Exit %s(%s)" % (self, args)

x = 上下文()

@和
定义_(它= x):
    打印 "Body %s" % 它

@和
定义_(它= x):
    打印 "%s 之前的正文" % it
    提出“无”
    打印 "%s 之后的正文" % it

于 2010-08-19T17:43:48.510 回答