Как декоратор Python может изменять вызовы в оформленной функции? - PullRequest
0 голосов
/ 28 октября 2019

Я не могу понять, как это сделать, и, честно говоря, я не знаю, возможно ли это.

Я хочу написать декоратор, который меняет способ вызова функции. Проще всего это увидеть на примере кода:

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 декоратор с такой функциональностью?

Теперь мне это не нужно, если оно слишком сложное,Я просто не могу понять, как это сделать простым способом.

Является ли проблема такого рода тривиальной? Или это действительно сложно?

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Вы можете создать новую функцию с обновленным глобальным словарем, чтобы для этой функции казалось, что глобальная привязка была к желаемому значению.

Обратите внимание, что это слабее, чем фактическая динамическая область, поскольку любые функции, вызываемые этой функцией, будут видеть исходные привязки, а не измененную.

См. namespaced_function, на который есть ссылка в КакТипы Python. FunctionType создавать динамические функции?

1 голос
/ 28 октября 2019

Чтобы уточнить ответ @ Дана Д. , вы должны создать новый функциональный объект для замены оригинала, что-то вроде этого:

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, но здесь это немного выходит за рамки.

IВы обновили вашу print реализацию, чтобы она была немного более общей, и сделали ввод в функцию декоратора более питоническим (меньше 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.
...