python 装饰器如何更改装饰函数中的调用?
how can a python decorator change calls in decorated function?
我不知道该怎么做,坦率地说,我不知道这是否可行。
我想写一个装饰器来改变函数的调用方式。使用示例代码最容易看到:
def my_print(*args, **kwargs):
print(args[0].upper())
@reroute_decorator('print', my_print)
def my_func():
print('normally this print function is just a print function...')
print('but since my_func is decorated with a special reroute_decorator...')
print('it is replaced with a different function, and its args sent there.')
my_func()
# NORMALLY THIS PRINT FUNCTION IS JUST A PRINT FUNCTION...
# BUT SINCE MY_FUNC IS DECORATED WITH A SPECIAL REROUTE_DECORATOR...
# IT IS REPLACED WITH A DIFFERENT FUNCTION, AND ITS ARGS SENT THERE.
在 python 中甚至可以使用具有这种功能的装饰器吗?
现在,如果它太复杂,我真的不需要它,我只是想不出如何用简单的方法来做。
这种问题很简单吗?还是真的很复杂?
要详细说明 ,您将创建一个新的函数对象来替换原来的对象,如下所示:
from types import FunctionType
def reroute_decorator(**kwargs):
def actual_decorator(func):
globals = func.__globals__.copy()
globals.update(kwargs)
new_func = FunctionType(
func.__code__, globals, name=func.__name__,
argdefs=func.__defaults__, closure=func.__closure__)
new_func.__dict__.update(func.__dict__)
return new_func
return actual_decorator
这里唯一的问题是更新后的函数对象是 唯一的 会看到您传入的任何 kwargs
的对象,因为它们将被欺骗到全局变量中。此外,您在调用装饰器函数后对模块所做的任何修改对装饰器函数都是不可见的,但这应该不是问题。您可以更深入地创建一个代理字典,使您能够与原始字典正常交互,但您明确定义的键除外,例如 print
,但这有点超出此处的范围。
我已经将您的 print
实现更新得更通用一些,并使装饰器函数的输入更符合 pythonic(更少 MATLABy):
def my_print(*args, **kwargs):
print(*(str(x).upper() for x in args), **kwargs)
@reroute_decorator(print=my_print)
def my_func():
print('normally this print function is just a print function...')
print('but since my_func is decorated with a special reroute_decorator...')
print('it is replaced with a different function, and its args sent there.')
这导致:
>>> my_func()
NORMALLY THIS PRINT FUNCTION IS JUST A PRINT FUNCTION...
BUT SINCE MY_FUNC IS DECORATED WITH A SPECIAL REROUTE_DECORATOR...
IT IS REPLACED WITH A DIFFERENT FUNCTION, AND ITS ARGS SENT THERE.
您可以使用更新的全局字典创建一个新函数,以便该函数看起来全局已绑定到所需的值。
请注意,这比实际的动态作用域弱,因为该函数调用的任何函数都将看到原始绑定,而不是修改后的绑定。
参见
中引用的 namespaced_function
我不知道该怎么做,坦率地说,我不知道这是否可行。
我想写一个装饰器来改变函数的调用方式。使用示例代码最容易看到:
def my_print(*args, **kwargs):
print(args[0].upper())
@reroute_decorator('print', my_print)
def my_func():
print('normally this print function is just a print function...')
print('but since my_func is decorated with a special reroute_decorator...')
print('it is replaced with a different function, and its args sent there.')
my_func()
# NORMALLY THIS PRINT FUNCTION IS JUST A PRINT FUNCTION...
# BUT SINCE MY_FUNC IS DECORATED WITH A SPECIAL REROUTE_DECORATOR...
# IT IS REPLACED WITH A DIFFERENT FUNCTION, AND ITS ARGS SENT THERE.
在 python 中甚至可以使用具有这种功能的装饰器吗?
现在,如果它太复杂,我真的不需要它,我只是想不出如何用简单的方法来做。
这种问题很简单吗?还是真的很复杂?
要详细说明
from types import FunctionType
def reroute_decorator(**kwargs):
def actual_decorator(func):
globals = func.__globals__.copy()
globals.update(kwargs)
new_func = FunctionType(
func.__code__, globals, name=func.__name__,
argdefs=func.__defaults__, closure=func.__closure__)
new_func.__dict__.update(func.__dict__)
return new_func
return actual_decorator
这里唯一的问题是更新后的函数对象是 唯一的 会看到您传入的任何 kwargs
的对象,因为它们将被欺骗到全局变量中。此外,您在调用装饰器函数后对模块所做的任何修改对装饰器函数都是不可见的,但这应该不是问题。您可以更深入地创建一个代理字典,使您能够与原始字典正常交互,但您明确定义的键除外,例如 print
,但这有点超出此处的范围。
我已经将您的 print
实现更新得更通用一些,并使装饰器函数的输入更符合 pythonic(更少 MATLABy):
def my_print(*args, **kwargs):
print(*(str(x).upper() for x in args), **kwargs)
@reroute_decorator(print=my_print)
def my_func():
print('normally this print function is just a print function...')
print('but since my_func is decorated with a special reroute_decorator...')
print('it is replaced with a different function, and its args sent there.')
这导致:
>>> my_func()
NORMALLY THIS PRINT FUNCTION IS JUST A PRINT FUNCTION...
BUT SINCE MY_FUNC IS DECORATED WITH A SPECIAL REROUTE_DECORATOR...
IT IS REPLACED WITH A DIFFERENT FUNCTION, AND ITS ARGS SENT THERE.
您可以使用更新的全局字典创建一个新函数,以便该函数看起来全局已绑定到所需的值。
请注意,这比实际的动态作用域弱,因为该函数调用的任何函数都将看到原始绑定,而不是修改后的绑定。
参见
namespaced_function