装饰Python内上下文管理功能

2024-06-16 11:13:41 发布

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

我想创建一个python上下文管理器,它允许以下操作(如果函数是string,那么reverse_decorator应用带第一个参数的decorated函数):

print('hi')
with MyFunctionDecorator('print', reverse_decorator):
    print('hello')
print('bye')

导致:

^{pr2}$

关键不是print函数本身,而是编写这种上下文管理器,它可以修饰任何函数—本地、全局、内置,来自任何模块。这在python中是可能的吗?我该怎么做?在

编辑:为了澄清一点,重点是不必在上下文中更改代码。在


Tags: 函数hello管理器参数stringwithdecoratorhi
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')

输出:

^{pr2}$

这应该适用于函数和其他模块等等,因为它修改了模块或类的属性。类方法很复杂,因为一旦你创建了一个类的实例,它的属性指向对象创建时在类中定义的函数,所以你必须在修改特定实例的行为还是修改托管上下文中新实例的行为之间做出选择,如示例所示。另外,试图修饰诸如listdict之类的内置类的方法是行不通的。在

如果您对其进行修改并添加一点:

print('hi')
with MyFunctionDecorator(print, reverse_decorator) as print:
    print('hello')
print('bye')

下面是一个适用于此示例的定义*:

^{pr2}$

但很可能只需显式地执行,在Python Zen之后:

print('hi')
print('hello'[::-1])
print('bye')

*此代码在许多情况下都不起作用,如the comments中所述:

  • 内部函数
  • 如果要修饰的函数是用import x from y as z导入的
  • 如果您关心之后您在globals()中定义了一个print函数,而不是直接作为一个内置函数

因为这更多的是一个概念的证明,是的,我们可以在这个例子中编写一个decorator,我不会试图修复这些缺点。使用我上面给出的方法,或者只使用装饰:

print('hi')
reverse_decorator(print)('hello')
print('bye')

相关问题 更多 >