将参数传递给去上下文装饰器 [英] Passing arguments to decontext decorator
问题描述
我有一个帮助器类 Decontext
,该类用于将上下文管理器转换为装饰器(pyton 2.6)。
I have a helper class Decontext
that I am using to turn a context manager into a decorator (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
接受一个参数,而我正在尝试弄清楚使用此装饰器时如何传递该参数?
My contextmanager
takes an argument and I am trying to figure out how to pass that argument when using this decorator ?
@contextmanager
def sample_t(arg1):
print "<%s>" % arg1
yield
这是我使用失败的方式:
This is how I am using it which fails:
my_deco = Decontext(sample_t)
@my_deco(arg1='example')
def some_func()
print 'works'
编辑:
我希望 Decontext
类在<$ c时传递context_manager中的所有 * args
$ c> __ call __ 函数执行。
I would like the Decontext
class to pass all *args
in the context_manager when the __call__
function executes.
示例:
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
后,
推荐答案
您遇到的问题是您在 __ init __
方法中设置的 _cm
属性实际上并未存储上下文管理器实例,而是上下文管理器的类型(或者可能是产生上下文管理器实例的工厂函数)。您需要稍后调用类型或工厂来获取实例。
The issue you're having is that the _cm
attribute you're setting up in your __init__
method isn't actually storing a context manager instance, but rather the the type of the context manager (or possibly a factory function that produces context manager instances). You need to call the type or factory later, to get an instance.
尝试一下,这对两个上下文管理器实例都适用(假定它们也不可调用)和需要参数的上下文管理器类型:
Try this, which should work for both context manager instances (assuming they're not also callable) and context manager types that require arguments:
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
那里有一个相当愚蠢的嵌套层次,但这正是您想要调用该事物的方式所需要的。这是一个运行中的示例:
There's a fairly silly level of nesting going on in there, but it's what you need given the way you want to call the thing. Here's an example with it in action:
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
作为上下文管理器将不起作用(类似于将与foo
一起使用将不起作用)。在 Decontext
实例上使用上下文管理器协议只有在使用上下文管理器实例(而不是类型或工厂)初始化或已经被调用时才有效。一个带有适当参数的装饰器。
Note that trying to use foo_deco_factory
as a context manager will not work (similarly to how using with foo
won't work). Using the context manager protocol on a Decontext
instance will work only if it was initialized with a context manager instance (rather than a type or factory) or if it has been called already as a decorator with appropriate arguments.
如果您不需要装饰器本身就可以充当上下文管理器,则可以很容易地将整个类变成一个函数( __ call __
成为一个额外的关闭级别,而不是一个方法):
If you didn't need the decorator to be able to act as a context manager itself, you could pretty easily turn the whole class into a function (with __call__
becoming an extra level of closure, rather than a method):
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
在这种情况下,为了简化代码,我总是假设您要传入一个上下文管理器工厂,而不是一个上下文管理器实例。
To simplify the code in this case, I always assume you're passing in a context manager factory, rather than a context manager instance.
这篇关于将参数传递给去上下文装饰器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!