декоратор внутри класса Python

1

Извините за мой английский. Я хочу создать метод декоратора, который может проверять каждый метод шагов и записывать его db.

Это мой метод:

class Test:

    @StepStatusManager.logger_steps("GET_LIST") # TypeError: logger_steps() missing 1 required positional argument: 'type'
    def get_mails(self):
       print("GET_MAIL")    

Это мой класс декоратора:

class StepStatusManager:

    def __init__(self):
        self.db = DB()

    def logger_steps(self, type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    self.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps
  • 0
    Кстати, не очень хорошая идея скрывать имя встроенного type .
Теги:
decorator

1 ответ

5
Лучший ответ

Вы пытаетесь вызвать метод экземпляра, logger_steps, непосредственно из класса StepStatusManager, а Python принимает значение "GET_LIST" как параметр self вместо type. Вы должны создать экземпляр StepStatusManager а затем сделать вместо этого декоратор, вызывающий метод экземпляра. Это может быть просто:

manager = StepStatusManager()

class Test:
    @manager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

Теперь создается экземпляр класса, а затем вызывается метод в экземпляре вместо того, чтобы пытаться вызвать метод непосредственно из класса. Теперь вы можете использовать manager для украшения как можно большего количества методов. Кроме того, это приведет к тому, что все декорированные методы будут использовать один и тот же StepStatusManager, но если вы хотите, вы можете создавать разные экземпляры и использовать их для украшения разных методов; что позволит вам использовать разные self.db для разных методов, если вам это нужно.

Другим подходом может быть переменная db в классе и вместо этого сделать logger_steps классом:

class StepStatusManager:

    db = DB()

    @classmethod
    def logger_steps(cls, type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    cls.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

Обратите внимание, однако, что это менее гибко, поскольку это не позволит вам иметь методы, украшенные разными менеджерами, если вам когда-нибудь понадобится. Кроме того, это в основном эквивалентно тому, что вместо класса используется модуль StepStatusManager, где db является модульной переменной, а logger_steps - это функция модуля, и это, вероятно, будет более ясным, если вы хотите эту функциональность:

# StepStatusManager.py

# ...

db = DB()

def logger_steps(type):
    def logger_steps(func):
        @functools.wraps(func)
        def wrapper(*args):
            try:
                func(*args)
                cls.db.setStatus(type)
            except BaseException as e:
                print(e)

        return wrapper

    return logger_steps

# test.py

import StepStatusManager

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

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


РЕДАКТИРОВАТЬ:

Просто для полноты и сравнения, вот еще одна версия, похожая на ту, что с @classmethod, но вместо этого использует @staticmethod (чтобы понять тонкую разницу между этими двумя декораторами, проверьте один из многих SO-вопросов об этом, например, что такое разница между @staticmethod и @classmethod? или значением @classmethod и @staticmethod для начинающих?):

class StepStatusManager:

    db = DB()

    @staticmethod
    def logger_steps(type):
        def logger_steps(func):
            @functools.wraps(func)
            def wrapper(*args):
                try:
                    func(*args)
                    StepStatusManager.db.setStatus(type)
                except BaseException as e:
                    print(e)

            return wrapper

        return logger_steps

class Test:
    @StepStatusManager.logger_steps("GET_LIST")
    def get_mails(self):
       print("GET_MAIL")

Как это часто бывает с @classmethod и @staticmethod, разница весьма минимальна. Их поведение может отличаться, если вы используете наследование или используете метакласс или декоратор или что-то в этом роде, но в остальном они почти одинаковы.

  • 0
    Большое спасибо за ваш ответ!
  • 1
    @jdehesa: Мне просто интересно, если мы сделаем def logger_steps(self, type): @staticmethod , сработает ли код OP? Тогда нам не нужно передавать неявный первый аргумент
Показать ещё 5 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню