通过装饰器定义上下文管理器的更短方法?

问题描述 投票:0回答:3

使用

@contextlib.contextmanager()
创建装饰器时,我们必须编写

enter_action(...)
try:
    yield ...
finally:
   exit_action(...)

这 3 行只是为了(相当不美观)

try/yield/finally
结构。 为什么我们不能得到这样的东西呢?

enter_action(...)
with context_magic(...):  # equivalent to try/yield/finally
    exit_action(...)

这在技术上可行吗?

python contextmanager
3个回答
0
投票

实际上,有一个简单的解决方案: 我们可以简单地将 try/finally 包装委托给自定义装饰器:

import contextlib

def short_ctxmanager(func):
    @contextlib.contextmanager
    def wrapped(*args, **kws):
        gen = func(*args, **kws)
        out = next(gen)
        try:
            yield out
        finally:
            try:
                next(gen)
            except StopIteration:
                pass
            else:
                raise RuntimeError("Context manager cannot have more than 1 yield.")
    return wrapped

@short_ctxmanager
def my_ctx():
    print("enter")
    yield
    print("exit (always executed)")

def test():
    with my_ctx():
        raise RuntimeError("exit should still run")

test()

打印:

enter
exit (always executed)
Traceback (most recent call last):
  File "/home/me/projects/ctx.py", line 29, in <module>
    test()
  File "/home/me/projects/ctx.py", line 27, in test
    raise RuntimeError("exit should still run")
RuntimeError: exit should still run

CC 为什么在使用 @contextmanager 装饰器时需要“try-finally”?

我认为我最初在问题中想象的

with
东西可能不起作用,因为我看不出有什么办法可以中断它来运行调用者的代码,然后再返回到退出处理程序。只有
yield
可以做到。


-1
投票

Contexts 提供一个目标(从

__enter__()
方法返回的值)和错误处理(通过
__exit__()
方法)。您的示例未处理这些功能。 因此,虽然可以设计一种可以运行此类示例的语言,但其上下文管理器的功能集将会减少。


-4
投票

在Python中,你可以定义一个上下文管理器。上下文管理器是一个定义

__enter__
__exit__
方法的类。例如:

class MyContextManager():
    def __init__(self, obj):
        self.obj = obj
    def __enter__(self):
        print(f'entering with object {self.obj}')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f'exiting with object {self.obj}')

if __name__ == '__main__':
    with MyContextManager(object()):
        print('inside block')
        

MyContextManager
类的任何实例都可以通过
with
语句来处理。
with
语句只是在执行
__enter__
语句块之前调用
with
方法,并在执行块之后调用
__exit__
__exit__
方法将传递
with
块内可能发生的任何异常的类型、值和回溯。 现在,要实现
yield
的目标,实际上将其放入
__enter__
方法中可能会很有用,尽管这是否有效取决于某些条件。

def enter_action(*args, **kwargs):
    """ enter action's implementation goes here"""
    pass
def exit_action(*args, **kwargs):
    """ exit action's implementation goes here"""
    pass
class ContextMagic():
    def __init__(self, *args,**kwargs):  # take what ever arguments that you need
        pass
    def __enter__(self): # call a function or do whatever you want
        enter_action()
    def __exit__(self, e_type, e_val, e_tb):
        exit_action()
if __name__ == '__main__':
    with ContextMagic(args, kwargs):
        # code goes here
© www.soinside.com 2019 - 2024. All rights reserved.