将参数传递给decontext d

2024-04-20 07:40:34 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个helper类Decontext,我正使用它将上下文管理器转换为decorator(pyton2.6)。在

class Decontext(object):
    """
    makes a context manager also act as decorator
    """

    def __init__(self, context_manager):
        self._cm = context_manager

    def __enter__(self):
        return self._cm.__enter__()

    def __exit__(self, *args, **kwds):
        return self._cm.__exit__(*args, **kwds)

    def __call__(self, func):
        def wrapper(*args, **kwds):
            with self:
                return func(*args, **kwds)

        return wrapper

我的contextmanager接受了一个参数,我正试图找出在使用这个修饰符时如何传递该参数?在

^{pr2}$

这就是我如何使用它,但失败了:

my_deco =  Decontext(sample_t)

@my_deco(arg1='example')
def some_func()
     print 'works'

编辑:

我希望Decontext类在执行__call__函数时传递上下文管理器中的所有*args。在

示例:

decorator_example = Decontext(sample_t) // I don't want to pass in the argument here but when the decorator is created. How can I modify my class to make this enhancement

编辑2:

我期望的例子

my_deco =  Decontext(sample_t)

@my_deco(arg1='example')
def some_func()
     print 'works'

预期产量:

'example' // running and passing argument to context_manager
'works' // after yield executing some_func 

Tags: sampleselfreturnexamplemydefcontextmanager
3条回答

您遇到的问题是您在__init__方法中设置的_cm属性实际上并不是存储上下文管理器实例,而是存储上下文管理器的类型(或者可能是生成上下文管理器实例的工厂函数)。稍后需要调用类型或工厂以获取实例。在

试试这个方法,它既适用于上下文管理器实例(假设它们不可调用)也适用于需要参数的上下文管理器类型:

class Decontext(object):
    """
    makes a context manager also act as decorator
    """

    def __init__(self, context_manager):
        self._cm = context_manager   # this may be a cm factory or type, but that's OK

    def __enter__(self):
        return self._cm.__enter__()

    def __exit__(self, *args, **kwds):
        return self._cm.__exit__(*args, **kwds)

    def __call__(self, *cm_args, **cm_kwargs):
        try:
            self._cm = self._cm(*cm_args, **cm_kwargs) # try calling the cm like a type
        except TypeError:
            pass
        def decorator(func):
            def wrapper(*args, **kwds):
                with self:
                    return func(*args, **kwds)

            return wrapper
        return decorator

在那里有一个相当愚蠢的嵌套层次,但这是你所需要的,因为你想称之为事物。下面是一个实际应用的例子:

^{pr2}$

它将输出:

entered foo("bar")
called baz("quux")
exited foo("bar")

请注意,尝试使用foo_deco_factory作为上下文管理器是行不通的(类似于使用with foo也不起作用)。只有使用上下文管理器实例(而不是类型或工厂)初始化了Decontext实例,或者它已经被调用为具有适当参数的装饰器时,才能在该实例上使用上下文管理器协议。在

如果您不需要decorator来充当上下文管理器,那么您可以很容易地将整个类转换为一个函数(其中__call__将成为一个额外的闭包级别,而不是一个方法):

def decontext(cm_factory):
    def factory(*cm_args, **cm_kwargs):
        cm = cm_factory(*cm_args, **cm_kwargs)
        def decorator(func):
            def wrapper(*args, **kwargs):
                with cm:
                    return func(*args, **kwargs)
            return wrapper
        return decorator
    return factory

为了简化这种情况下的代码,我总是假设您传递的是上下文管理器工厂,而不是上下文管理器实例。在

如果我理解正确,你应该:

@my_deco
def func(arg1):
   print "blah"

装饰者必须装饰一些东西(比如函数)。但是,你的类还有一些其他问题,但我不确定如何解决它们,因为它有点难以理解它应该做什么。在

我想以下就是你要找的。主要的关键是您不能将您想要的上下文管理器参数直接传递给您的Decontext类上的__call__方法,所以我们使用一个helper函数来实现这一点。有可能有一种方法可以简化这一点(但我将把它留给读者作为练习:)

from contextlib import contextmanager
from functools import partial

class _Decontext(object):
    """
    makes a context manager also act as decorator
    """

    def __init__(self, cm, *args, **kwargs):
        self._cm = cm
        self.args = args
        self.kwargs = kwargs

    def __call__(self, func):

        def wrapper(*args, **kwds):    
            with self._cm(*self.args, **self.kwargs):
                func(*args, **kwds)

        return wrapper

# helper for our helper :)
def decontext(cm=None):
    def wrapper(cm, *args, **kwargs):
        return _Decontext(cm, *args, **kwargs)
    return partial(wrapper, cm)


@contextmanager
def sample_t(arg1):
    print "<%s>" % arg1
    yield 

my_deco = decontext(sample_t)

@my_deco(arg1='example')
def some_func():
     print 'works'

if __name__ == '__main__':    
    some_func()

该输出:

^{pr2}$

相关问题 更多 >