Извините за мой английский. Я хочу создать метод декоратора, который может проверять каждый метод шагов и записывать его 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
Вы пытаетесь вызвать метод экземпляра, 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
, разница весьма минимальна. Их поведение может отличаться, если вы используете наследование или используете метакласс или декоратор или что-то в этом роде, но в остальном они почти одинаковы.
def logger_steps(self, type):
@staticmethod
, сработает ли код OP? Тогда нам не нужно передавать неявный первый аргумент
type
.