这可能会把事情推得太远,但主要是出于好奇......
是否可以让一个可调用的对象(函数/类)同时充当 一个上下文管理器和一个装饰器:
def xxx(*args, **kw):
# or as a class
@xxx(foo, bar)
def im_decorated(a, b):
print('do the stuff')
with xxx(foo, bar):
print('do the stuff')
答案 0 :(得分:41)
从Python 3.2开始,对此的支持甚至包含在标准库中。从类contextlib.ContextDecorator
派生,可以很容易地编写可以用作装饰器或上下文管理器的类。此功能可以轻松地向后移植到Python 2.x - 这是一个基本实现:
class ContextDecorator(object):
def __call__(self, f):
@functools.wraps(f)
def decorated(*args, **kwds):
with self:
return f(*args, **kwds)
return decorated
从此课程中派生您的上下文管理员,并照常定义__enter__()
和__exit__()
方法。
答案 1 :(得分:21)
在Python 3.2+中,您可以使用@contextlib.contextmanager
定义一个也是装饰器的上下文管理器。
来自文档:
contextmanager()
使用ContextDecorator
,因此它创建的上下文管理器可用作装饰器以及with
语句
使用示例:
type
TClientData = record
UserName: string;
end;
...
// during login...
var
ClientData: TClientData;
begin
New(ClientData);
ClientData.UserName := ...; // read from the client
Socket.Data := ClientData;
end;
...
// during logout/disconnect...
var
ClientData: TClientData;
begin
ClientData := Socket.Data;
Socket.Data := nil;
Dispose(ClientData);
end;
...
// during private messaging
var
client: TCustomWinSocket;
begin
for I := 0 to ServerSocket1.Socket.ActiveConnections-1 do
begin
client := ServerSocket1.Socket.Connections[i];
if TClientData(client.Data).UserName = msgUser then
begin
client.SendText(msg);
break;
end;
end;
end;
答案 2 :(得分:12)
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
现在你可以这样做:
mydeco = Decontext(some_context_manager)
并允许两者
@mydeco
def foo(...):
do_bar()
foo(...)
和
with mydeco:
do_bar()
答案 3 :(得分:3)
以下是一个例子:
class ContextDecorator(object):
def __init__(self, foo, bar):
self.foo = foo
self.bar = bar
print "init", foo, bar
def __call__(self, f):
print "call"
def wrapped_f():
print "about to call"
f()
print "done calling"
return wrapped_f
def __enter__(self):
print "enter"
def __exit__(self, exc_type, exc_val, exc_tb):
print "exit"
with ContextDecorator(1, 2):
print "with"
@ContextDecorator(3, 4)
def sample():
print "sample"
sample()
打印:
init 1 2
enter
with
exit
init 3 4
call
about to call
sample
done calling
答案 4 :(得分:1)
尽管我在这里同意(并赞成)@jterrace,但我添加了一个非常细微的变体,该变体返回装饰的函数,并包括装饰器和装饰的函数的参数。
class Decon:
def __init__(self, a=None, b=None, c=True):
self.a = a
self.b = b
self.c = c
def __enter__(self):
# only need to return self
# if you want access to it
# inside the context
return self
def __exit__(self, exit_type, exit_value, exit_traceback):
# clean up anything you need to
# otherwise, nothing much more here
pass
def __call__(self, func):
def decorator(*args, **kwargs):
with self:
return func(*args, **kwargs)
return decorator