我不知道该怎么做,坦白说,我不知道是否可能。
我想写一个装饰器来改变函数的调用方式。用示例代码最容易看到:
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中使用?
现在,如果它太复杂,我真的不需要它,我只是想不出简单的方法来做。
这种问题琐碎吗?还是真的很复杂?
答案 0 :(得分:2)
您可以使用更新的全局字典创建新函数,以便该函数看起来全局已绑定到所需的值。
请注意,这比实际的动态作用域弱,因为该函数调用的任何函数都将看到原始绑定,而不是修改后的绑定。
请参见How does Python's types.FunctionType create dynamic Functions?中引用的namespaced_function
答案 1 :(得分:1)
要详细介绍@Dan D.'s answer,您需要创建一个新的函数对象来替换原始函数对象,如下所示:
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
这里唯一要注意的是,更新的函数对象是 only ,它将看到您传入的任何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.