我想创建一个python上下文管理器,它允许以下内容(如果是字符串,则reverse_decorator应用带有第一个参数的装饰函数):
print('hi')
with MyFunctionDecorator('print', reverse_decorator):
print('hello')
print('bye')
导致:
hi
olleh
bye
关键不是打印函数本身,而是编写这种上下文管理器,它可以装饰任何函数 - 本地,全局,内置,来自任何模块。这在python中甚至可能吗?我该怎么办?
编辑:为了澄清一点,重点是不必使用上下文更改中的代码。
答案 0 :(得分:2)
这是我的方法:
from contextlib import contextmanager
from importlib import import_module
@contextmanager
def MyFunctionDecorator(func, decorator):
if hasattr(func, '__self__'):
owner = func.__self__
elif hasattr(func, '__objclass__'):
owner = func.__objclass__
else:
owner = import_module(func.__module__)
qname = func.__qualname__
while '.' in qname:
parent, qname = qname.split('.', 1)
owner = getattr(owner, parent)
setattr(owner, func.__name__, decorator(func))
yield
setattr(owner, func.__name__, func)
# Example decorator, reverse all str arguments
def reverse_decorator(f):
def wrapper(*args, **kwargs):
newargs = []
for arg in args:
newargs.append(arg[::-1] if isinstance(arg, str) else arg)
newkwargs = {}
for karg, varg in kwargs.values():
newkwargs[karg] = varg[::-1] if isinstance(varg, str) else varg
return f(*newargs, **newkwargs)
return wrapper
# Free functions
print('hi')
with MyFunctionDecorator(print, reverse_decorator):
print('hello')
print('bye')
# Class for testing methods (does not work with builtins)
class MyClass(object):
def __init__(self, objId):
self.objId = objId
def print(self, arg):
print('Printing from object', self.objId, arg)
# Class level (only affects instances created within managed context)
# Note for decorator: first argument of decorated function is self here
with MyFunctionDecorator(MyClass.print, reverse_decorator):
myObj = MyClass(1)
myObj.print('hello')
# Instance level (only affects one instance)
myObj = MyClass(2)
myObj.print('hi')
with MyFunctionDecorator(myObj.print, reverse_decorator):
myObj.print('hello')
myObj.print('bye')
输出:
hi
olleh
bye
Printing from object 1 olleh
Printing from object 2 hi
Printing from object 2 olleh
Printing from object 2 bye
这应该适用于函数和其他模块等,因为它修改了模块或类的属性。类方法很复杂,因为一旦创建了类的实例,它的属性就指向创建对象时类中定义的函数,所以你必须在修改一个类的行为之间做出选择。特定实例或修改托管上下文中新实例的行为,如示例中所示。此外,尝试修饰内置类的方法(如list
或dict
)也不起作用。
答案 1 :(得分:1)
如果你修改它可以添加一点:
print('hi')
with MyFunctionDecorator(print, reverse_decorator) as print:
print('hello')
print('bye')
这是一个适用于此示例的定义*:
def reverse_decorator(func):
def wrapper(*args, **kwargs):
if len(args) == 1 and not kwargs and isinstance(args[0], str):
return func(args[0][::-1])
return func(*args, **kwargs)
return wrapper
class MyFunctionDecorator:
def __init__(self, func, decorator):
self.func = func
self.decorator = decorator
def __enter__(self):
"""Return the decorated function"""
return self.decorator(self.func)
def __exit__(self, *args):
"""Reset the function in the global namespace"""
globals()[self.func.__name__] = self.func
但是,在Python Zen:
之后,明确地执行此操作可能更容易print('hi')
print('hello'[::-1])
print('bye')
*此代码在许多情况下不起作用,@AranFey中注明the comments:
import x from y as z
print
中定义了globals()
功能,而不是直接作为内置因为这更像是一个概念验证,是的,人们可以写一个在这个例子中起作用的装饰器,我不会试图解决这些缺点。只需使用我上面给出的方式,或只使用装饰器:
print('hi')
reverse_decorator(print)('hello')
print('bye')