Нет объекта класса , пока , когда выполняются декораторы функций в теле класса.Сначала выполняется тело класса, , а затем класс создается.
Вместо того, чтобы декоратор искал атрибут класса для изменения, добавьте атрибут в оформленный объект функции.Метакласс, или _validate()
реализация затем ищет любые объекты с этим атрибутом и добавляет их в список после создания объекта класса.
Я собираюсь предположить, что выВы хотите сохранить порядок, в котором декораторы добавили бы украшенные элементы в список:
from itertools import count
class Validator(metaclass=MetaValidator):
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
и в метаклассе:
class MetaValidator(type):
def __new__(cls, name, bases, dct):
new_cls = super().__new__(cls, name, bases, dct)
registered = []
for v in dct.values():
id = getattr(v, '_validator_function_id', None)
if id is None and isinstance(v, (staticmethod, classmethod)):
# unwrap staticmethod or classmethod decorators
id = getattr(v.__func__, '_validator_function_id', None)
if id is not None:
registered.append((id, v))
new_cls.valid_funcs = [f for _, f in sorted(registered)]
return new_cls
Обратите внимание, что если вы используетеPython 3.6 или новее, тогда вам больше не нужен метакласс.Вы можете поместить ту же логику в метод class.__init_subclass__
.
Обратите внимание, что здесь регистрируется несвязанных объектов .Для staticmethod
объектов это означает, что вызов не будет выполнен с:
TypeError: <staticmethod object at 0x10d1b7048> is not a callable object
Возможно, вы захотите зарегистрировать атрибут __func__
в этом случае или использовать .__get__
, чтобы «привязать» объект к чему-либо(a staticmethod
игнорирует контекст привязки в любом случае) `.
Если вы явно привязываете метод _validate()
, вам не нужно использовать staticmethod
объекты:
def _validate(self, **kwargs):
for f in self.valid_funcs:
bound = f.__get__(self)
signature = inspect.signature(bound)
bound(**{name: kwargs[name] for name in signature.parameters})
Теперь @validator.add
будет работать с staticmethod
, classmethod
и обычными функциями.
И если у вас есть метод _validate()
, ищущий методы, тогда связывание может быть сделано для вас .Вы можете выбрать поддержку наследования, просто используя dir()
и getattr()
:
from operator import itemgetter
from itertools import count
class Validator:
@classmethod
def add(cls, f):
_count = getattr(Validator.add, '_count', None)
if _count is None:
_count = Validator.add.__func__._count = count()
f._validator_function_id = next(_count)
return f
def _list_validators(self):
objects = (getattr(self, name) for name in dir(self))
return sorted(
(o for o in objects if hasattr(o, '_validator_function_id')),
key=attrgetter('_validator_function_id'))
def _validate(self, **kwargs):
for f in self._list_validators():
signature = inspect.signature(f)
f(**{name: kwargs[name] for name in signature.parameters})
getattr()
дает вам связанный объект, дальнейшее связывание не требуется.