改进“with”的直接替换Python 2.4 的声明

发布于 2024-08-07 13:02:59 字数 144 浏览 2 评论 0原文

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

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

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

Can you suggest a way to code a drop-in replacement for the "with" statement that will work in Python 2.4?

It would be a hack, but it would allow me to port my project to Python 2.4 more nicely.

EDIT:
Removed irrelevant metaclass sketch

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

久伴你 2024-08-14 13:02:59

只需使用 try-finally 即可。

确实,这作为一种心理练习可能很好,但如果你真的在你关心的代码中这样做,你最终会得到丑陋的、难以维护的代码。

Just use try-finally.

Really, this may be nice as a mental exercise, but if you actually do it in code you care about you will end up with ugly, hard to maintain code.

指尖微凉心微凉 2024-08-14 13:02:59

我认为你可以(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 行 in: 替换

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

为装饰函数“声明” in:

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

但是获得相同的行为(执行 do_something_... 代码)。请注意,装饰器将函数声明更改为立即调用,这有点邪恶。

You could (ab)use decorators to do this, I think. The following works, eg:

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,

(Well, in Python 2.4 file objects don't have __enter__ and __exit__ methods, but otherwise it works)

The idea is you're replacing the with line in:

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

with the decorated function "declaration" in:

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

but getting the same behaviour (the do_something_... code executed). Note the decorator changes the function declaration into an immediate invocation which is more than a little evil.

高跟鞋的旋律 2024-08-14 13:02:59

由于您需要在错误期间和非错误期间退出上下文管理器,因此我认为不可能使用元类执行通用用例,或者实际上根本不可能。为此,您将需要 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)

Since you need to exit the context manager both during errors and not errors, I don't think it's possible to do a generic usecase with metaclasses, or in fact at all. You are going to need try/finally blocks for that.

But maybe it's possible to do something else in your case. That depends on what you use the context manager for.

Using __del__ can help in some cases, like deallocating resource, but since you can't be sure it gets called, it can only be used of you need to release resources that will be released when the program exits. That also won't work if you are handling exceptions in the __exit__ method.

I guess the cleanest method is to wrap the whole context management in a sort of context managing call, and extract the code block into a method. Something like this (untested code, but mostly stolen from 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)
可爱暴击 2024-08-14 13:02:59

这个怎么样?

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)

与不带 aswith 关键字并行。

或者:

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

它与 with 关键字与 as 类似。

How about this?

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

Usage:

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

Which parallels the with keyword without as.

Or:

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

Which parallels the with keyword with the as.

国产ˉ祖宗 2024-08-14 13:02:59

如果您同意使用 def 只是为了获取一个块和立即执行的装饰器,那么您可以使用函数签名来为指定的情况获得更自然的东西。

import sys
def with(func):
    def decorated(body = func):
        contexts = body.func_defaults
        try:
            exc = None, None, None
            try:
                for context in contexts:
                    context.__enter__()
                body()
            except:
                exc = sys.exc_info()
                raise
        finally:
            for context in reversed(contexts):
                context.__exit__(*exc)
    decorated()

class Context(object):
    def __enter__(self):
        print "Enter %s" % self
    def __exit__(self, *args):
        print "Exit %s(%s)" % (self, args)

x = Context()

@with
def _(it = x):
    print "Body %s" % it

@with
def _(it = x):
    print "Body before %s" % it
    raise "Nothing"
    print "Body after %s" % it

If you are OK with using def just to get a block, and decorators that immediately execute, you could use the function signature to get something more natural for the named case.

import sys
def with(func):
    def decorated(body = func):
        contexts = body.func_defaults
        try:
            exc = None, None, None
            try:
                for context in contexts:
                    context.__enter__()
                body()
            except:
                exc = sys.exc_info()
                raise
        finally:
            for context in reversed(contexts):
                context.__exit__(*exc)
    decorated()

class Context(object):
    def __enter__(self):
        print "Enter %s" % self
    def __exit__(self, *args):
        print "Exit %s(%s)" % (self, args)

x = Context()

@with
def _(it = x):
    print "Body %s" % it

@with
def _(it = x):
    print "Body before %s" % it
    raise "Nothing"
    print "Body after %s" % it

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文