您的当前位置:首页正文

python装饰器的另类用法

2024-11-11 来源:个人技术集锦

在对pyverilog源码进行单步调试时,遇到一个很奇怪的现象,被装饰器装饰的方法t_LINECOMMENT没有主动调用,但装饰器TOKEN中的内嵌函数set_regex却被调用了。

## lexer.py

from ply.lex import *


class VerilogLexer(object):
    linecomment = r"""//.*?\n"""

    @TOKEN(linecomment)
    def t_LINECOMMENT(self, t):
        t.lexer.lineno += t.value.count("\n")
        pass
## lex.py

def _get_regex(func):
    return getattr(func, 'regex', func.__doc__)

def TOKEN(r):
    def set_regex(f):
        if hasattr(r, '__call__'):
            f.regex = _get_regex(r)
        else:
            f.regex = r
        return f
    return set_regex

原因就在于装饰器TOKEN是带参数的。先执行TOKEN(linecomment)函数调用,返回set_regex对象,@TOKEN(linecomment)就变成@set_regex,这是一个新的装饰器,装饰t_LINECOMMENT,接着执行set_regex(t_LINECOMMENT),返回f对象,这里f对象就是t_LINECOMMENT,不是新创建的对象。这样就偷偷执行了set_regex方法。

@TOKEN(linecomment)
    def t_LINECOMMENT(self, t):pass

## 上面的代码等价于下面的代码

def t_LINECOMMENT(self, t):pass
t_LINECOMMENT = TOKEN(linecomment)(t_LINECOMMENT)

这种装饰器的用法可用于为函数增加属性: 

Top