Измените глобальную переменную, которая используется в качестве аргумента по умолчанию в декораторе в python - PullRequest
0 голосов
/ 22 апреля 2020

У меня есть функция, которая использует глобальную переменную x в качестве значения по умолчанию для своего аргумента. Однако в функции main(), вызываемой в блоке if __name__ == '__main__':, я хотел бы разрешить пользователю изменять эту переменную, чтобы изменить поведение функции. Это не похоже на работу, потому что когда я запускаю скрипт x в функции, в этом случае уже занят 9 и не изменяется на 10, как я хотел бы.

x = 9

def function(var=x):
    print(var)

def main():
    global x
    x = 10
    function()

if __name__ == '__main__':
    main()

Простой вызов function(var=x) в main() не будет работать в моем случае, потому что function() на самом деле декоратор, и это скорее:

from module import decorator

x = 9

@decorator(var1=x, var2=15)
def function():
    pass

def main():
    global x
    x = 10
    function()

if __name__ == '__main__':
    main()

Кроме того, декоратор импортируется из другого модуля, поэтому я думаю, что ответ, опубликованный в Определение аргумента по умолчанию в качестве глобальной переменной также может не сработать. Еще сложнее, предполагается, что декоратор принимает и другие аргументы, поэтому использование класса, предложенного kederra c ниже, также не работает. Однако, так как этот модуль сделан мной, я мог бы скопировать необходимый код в мой новый скрипт, но я хотел бы избежать этого, если это возможно.

Заранее большое спасибо за помощь!

Ответы [ 2 ]

1 голос
/ 22 апреля 2020

вы можете использовать class в качестве декоратора

class my_decorator:
    x = 9

    def __init__(self, f):
        self.f = f

    def __call__(self, *args, **kwargs):
            print(my_decorator.x)
            return self.f(*args, **kwargs)



@my_decorator
def function():
    pass

def main():
    my_decorator.x = 4
    function()

main()

# 4

, если вы хотите передать параметр вашему декоратору, вы можете использовать:

from functools import partial

class my_decorator:
    x = 9

    def __init__(self, f, param1):
        self.f = f
        self.param1 = param1

    def __call__(self, *args, **kwargs):
            print(my_decorator.x)
            print(self.param1)
            return self.f(*args, **kwargs)



@partial(my_decorator, param1='test_param')
def function():
    pass

def main():
    my_decorator.x = 4
    function()

main()

# 4
# test_param
0 голосов
/ 22 апреля 2020

Я нашел решение в Python Поваренной книге от Бизли и Джонса , глава 9.5. Они вводят функции доступа, которые могут изменять переменные с помощью nonlocal. Я приложил пример решения.

from functools import wraps, partial

def attach_wrapper(obj, func=None):
    if func is None:
        return partial(attach_wrapper, obj)
    setattr(obj, func.__name__, func)
    return func

def decorator(arg1, arg2):
    def decorate(func):
        @wraps(func)
        def wrapper(*args, **kwarg):
            ...
            do_something_with_arg1_and_arg2
            ...
            return func(*args, **kwargs)            

        @attach_wrapper(wrapper)
        def set_arg1(new_arg1):
            nonlocal arg1
            arg1 = new_arg1
        return wrapper
    return decorate

Так что теперь вы можете украсить свой function() с помощью decorator и затем изменить arg1, используя set_arg1() в качестве метода function(), например, так:

@decorator(arg1=9, arg2=15)
def function():
    pass

function.set_arg1(10)

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

...