我有一个帮助类Decontext
,我用它来将上下文管理器变成装饰器(pyton 2.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
接受了一个参数,我试图弄清楚在使用这个装饰器时如何传递该参数?
@contextmanager
def sample_t(arg1):
print "<%s>" % arg1
yield
这就是我使用它失败的原因:
my_deco = Decontext(sample_t)
@my_deco(arg1='example')
def some_func()
print 'works'
编辑:
我希望Decontext
类在*args
函数执行时传递context_manager中的所有__call__
。
示例:
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
答案 0 :(得分:2)
您遇到的问题是,您在_cm
方法中设置的__init__
属性实际上并不存储上下文管理器实例,而是上下文管理器的类型(或者可能是生成上下文管理器实例的工厂函数)。您需要稍后调用类型或工厂来获取实例。
尝试这个,它应该适用于两个上下文管理器实例(假设它们不可调用)和需要参数的上下文管理器类型:
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
那里有一个相当愚蠢的嵌套水平,但是根据你想要的方式,你需要它。以下是其中的一个示例:
from contextlib import contextmanager
@contextmanager
def foo(arg):
print("entered foo({!r})".format(arg))
yield
print("exited foo({!r})".format(arg))
foo_deco_factory = Decontext(foo)
@foo_deco_factory("bar")
def baz(arg):
print("called baz({!r})".format(arg))
baz("quux")
将输出:
entered foo("bar")
called baz("quux")
exited foo("bar")
请注意,尝试使用foo_deco_factory
作为上下文管理器不起作用(类似于使用with foo
无法工作的方式)。在Decontext
实例上使用上下文管理器协议只有在使用上下文管理器实例(而不是类型或工厂)初始化时,或者已经作为具有适当参数的装饰器调用时才会起作用。
如果您不需要装饰器能够充当上下文管理器本身,您可以很容易地将整个类转换为一个函数(__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
为了简化这种情况下的代码,我总是假设您要在上下文管理器工厂中传递,而不是传递给上下文管理器实例。
答案 1 :(得分:1)
我认为以下是您正在寻找的内容。主要的关键是你不能直接将你想要的上下文管理器参数传递给__call__
类的Decontext
方法,所以我们使用辅助函数来做到这一点。可能有一种方法可以简化这一点(但我会将其作为练习留给读者:))
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()
输出:
<example>
works
答案 2 :(得分:0)
如果我理解正确,你应该这样做:
@my_deco
def func(arg1):
print "blah"
装饰者必须装饰一些东西(比如一个函数)。但是,您的班级还有其他一些问题,但我不确定如何修复它们,因为它有点难以理解它应该做什么。