Python - Доступ к декораторам родительского класса внутри объявления класса - PullRequest
2 голосов
/ 15 марта 2019

Допустим, у меня есть этот класс:

class Foo:
    @classmethod
    def some_decorator(cls, ...):
        ...

А затем я создаю подкласс, который использует декоратор родительского класса:

class Bar(Foo):
    @Foo.some_decorator(...)
    def some_function(...)
        ...

Как убрать необходимость в Foo. перед именем декоратора? Приведенный ниже код не работает:

class Bar(Foo):
    @some_decorator(...)
    def some_function(...)
        ...

Я считаю, что это возможно, потому что это делает библиотека sly.

См. Их пример:

from sly import Lexer, Parser

class CalcLexer(Lexer):
    ...

    @_(r'\d+')
    def NUMBER(self, t):
        t.value = int(t.value)
        return t

    ...

Как видите, вы можете ввести @_(...) вместо @Lexer._(...).

Как они этого добиваются?

Ответы [ 2 ]

3 голосов
/ 16 марта 2019

Я заглянул внутрь библиотеки, о которой вы говорите, и класс Lexer наследует метакласс:

class Lexer(metaclass=LexerMeta):

Внутри LexerMeta вы можете найти следующее:

@classmethod
    def __prepare__(meta, name, bases):
        d = LexerMetaDict()

        def _(pattern, *extra):
            patterns = [pattern, *extra]
            def decorate(func):
                pattern = '|'.join(f'({pat})' for pat in patterns )
                if hasattr(func, 'pattern'):
                    func.pattern = pattern + '|' + func.pattern
                else:
                    func.pattern = pattern
                return func
            return decorate

        d['_'] = _
        d['before'] = _Before
        return d

Метакласс используется для создания объекта класса, который затем используется для создания экземпляров объектов. Из того, что я вижу в этом методе, можно сказать, что здесь d['_'] = _ этот метакласс динамически присоединяет метод _ к классу, который вы собираетесь использовать.

Это означает, что то, что они делают, мало чем отличается от:

class Bar:
    @staticmethod
    def some_decorator(f):
        ...

    @some_decorator
    def some_function(self):
        ...
3 голосов
/ 16 марта 2019

Это делается с помощью метакласса , который реализует метод __prepare__. Выдержка из документов:

3.3.3.4. Подготовка пространства имен класса

Как только соответствующий метакласс определен, класс пространство имен подготовлено. Если метакласс имеет атрибут __prepare__, это называется namespace = metaclass.__prepare__(name, bases, **kwds) (где дополнительные ключевые аргументы, если таковые имеются, приходят из класса определение).

Проще говоря: метод __prepare__ возвращает словарь, содержащий запись для декоратора. Подтверждение концепции:

class MyMeta(type):
    def __prepare__(name, bases):
        return {'x': 'foobar'}

class MyClass(metaclass=MyMeta):
    print(x)  # output: foobar
...