Python - 在类声明中访问父类装饰器

Posted

技术标签:

【中文标题】Python - 在类声明中访问父类装饰器【英文标题】:Python - Accessing parent class decorators inside class declaration 【发布时间】:2019-08-07 00:05:35 【问题描述】:

假设我有这个课程:

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._(...)

他们是如何做到这一点的?

【问题讨论】:

旁白:我不太喜欢这种设计选择。在类中神奇地产生一个装饰器会产生不明显、难以理解的代码。一个更清洁的解决方案是让用户使用from sly import _ 导入_ 装饰器。三思而后行,是否要在代码库中包含它。 @Aran-Fey 在我看来,这也不是很好:你污染了全局命名空间,_ 不能对每个类都意味着不同的东西。对于 sly,_ 对于每个基类都有不同的含义。 我对这个项目一无所知,但词法分析器和解析器是为速度而不是可读性而构建的。这就是为什么 Lex 和 YACC 对某些人来说如此困难的原因。 【参考方案1】:

这是通过实现__prepare__ 方法的metaclass 完成的。文档摘录:

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

【讨论】:

谢谢。有没有办法像我的问题一样让装饰器成为@classmethod?我需要在装饰器中访问cls @DavidCallanan 好吧,当然可以将@classmethod 添加到装饰器中,但这不会让它访问类——它只会让它与TypeError: 'classmethod' object is not callable 一起崩溃。这在评论中有点难以回答,所以我可以请您发布一个关于它的新问题吗? ***.com/questions/55207813/…【参考方案2】:

我查看了您所说的库内部,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):
        ...

【讨论】:

以上是关于Python - 在类声明中访问父类装饰器的主要内容,如果未能解决你的问题,请参考以下文章

如何在类中声明装饰器,以装饰已经装饰的继承方法?

如何在类中定义装饰器?

python_如何在类中定义装饰器

TS之装饰器

python装饰器在类中的实现

TypeScript(21): 装饰器