Применить декоратор к методу класса, который обращается к атрибуту класса - PullRequest
2 голосов
/ 28 января 2020

Можно ли написать декоратор, который действует на метод класса и использует атрибуты класса? Например, я хотел бы добавить декоратор к функциям, которые будут возвращать ошибку, если один из атрибутов класса (который устанавливается, когда пользователь вызывает функцию), имеет значение False.

Например, моя попытка (неудачная) код, поскольку is_active не может получить доступ к методам MyClass):

def is_active(active):
    if active == False:
        raise Exception("ERROR: Class is inactive")


class MyClass():

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

    @is_active
    def foo(self, variable):
        print("foo")
        return variable

    @is_active
    def bar(self, variable):
        print("bar")
        return variable

, где ожидаемое поведение:

cls = MyClass(active=True)
cls.foo(42)
---> function prints "foo" and returns 42

cls = MyClass(active=False)
cls.foo(42)
---> function raises an exception as the active flag is False

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

Если вышеупомянутое возможно, мой дополнительный вопрос: возможно ли "скрыть" / удалить методы из экземпляра класса, основанного на этом флаге. Например, если пользователь создает экземпляр класса с помощью active=False, то когда он использует i Python и нажимает <tab>, он может видеть только те методы, которые разрешено использовать?

Спасибо вы.

1 Ответ

2 голосов
/ 28 января 2020

Декораторы могут сбивать с толку. Обратите внимание, что функция передается в качестве параметра, и декоратор ожидает, что функция (или вызываемый объект) будет возвращена. Так что вам просто нужно вернуть другую функцию. У вас есть все остальное, что вам нужно, поскольку self передается в качестве первого аргумента методу класса. Вам просто нужно добавить новую функцию в декоратор, которая делает то, что вы хотите.

def is_active_method(func):
    def new_func(*args, **kwargs):
        self_arg = args[0] # First argument is the self
        if not self_arg.active:
            raise Exception("ERROR: Class is inactive")
        return func(*args, **kwargs)
    return new_func


class MyClass():

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

    @is_active_method
    def foo(self, variable):
        print("foo")
        return variable

    @is_active_method
    def bar(self, variable):
        print("bar")
        return variable

m = MyClass(True) # Prints foo from the method
m.foo(2)

m = MyClass(False) # Outputs the exception
m.foo(2)
...