У меня около 20 различных классов, каждый из которых содержит несколько переменных класса. Переменные инициализируются как None
и загружается из разных конфигов в зависимости от выбранного режима. Образец кода:
class Class1:
VAR1 = VAR2 = VAR3 = VAR4 = None
def __init__(self):
pass
def method1(self):
pass
@staticmethod
def method2():
pass
Мне нужно вызвать исключение, если какой-либо из методов класса вызывается до загрузки всех переменных класса.
С самого начала я решил, что добавление условий к каждому методу — плохое решение, поэтому сделал следующие декораторы:
from functools import wraps
def for_all_methods(decorator):
""" Patched from https://stackoverflow.com/a/6307868/10824407 """
def decorate(cls):
for attr in cls.__dict__:
if callable(getattr(cls, attr)):
setattr(cls, attr, decorator(cls, getattr(cls, attr)))
return cls
return decorate
def check(variables=None, check_func=lambda x: x is not None):
def decorator(cls, func):
@wraps(func)
def wrapper(*args, **kwargs):
if variables is not None:
for variable_name in variables:
variable = getattr(cls, variable_name)
if variable is None or not check_func(variable):
raise Exception("Check failed")
return func(*args, **kwargs)
return wrapper
return decorator
Теперь я могу украсить все методы класса:
@for_all_methods(check(["VAR1", "VAR2", "VAR3", "VAR4"]))
class Class1:
VAR1 = VAR2 = VAR3 = VAR4 = None
def __init__(self):
pass
def method1(self):
pass
@staticmethod
def method2():
pass
Class1.method2() # throws an error
Class1.VAR1, Class1.VAR2, Class1.VAR3, Class1.VAR4 = range(4)
Class1.method2() # doesn't throw an error
Это работает и решает мою проблему, но я чувствую, что есть более элегантное решение.
Не могли бы вы мне посоветовать еще что-нибудь, что не требует серьезных изменений архитектуры? Может быть, есть какой-то способ метакласса, который я пропустил …