【问题标题】:When can you use a name before it's defined?什么时候可以在定义之前使用名称?
【发布时间】:2021-02-02 20:15:16
【问题描述】:

在SLY中有一个写计算器的例子(转载自calc.pyhere):

from sly import Lexer

class CalcLexer(Lexer):
    tokens = { NAME, NUMBER }
    ignore = ' \t'
    literals = { '=', '+', '-', '*', '/', '(', ')' }

    # Tokens
    NAME = r'[a-zA-Z_][a-zA-Z0-9_]*'

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

    @_(r'\n+')
    def newline(self, t):
        self.lineno += t.value.count('\n')

    def error(self, t):
        print("Illegal character '%s'" % t.value[0])
        self.index += 1

看起来它有问题,因为 NAMENUMBER 在定义之前就被使用了。但实际上,没有NameError,这段代码执行得很好。这是如何运作的?什么时候可以在名称被定义之前引用它?

【问题讨论】:

  • Lexer 使用处理缺失变量的自定义元类。
  • 这里有一些 deep 魔法——Lexer 有一个元类,它重新定义了它的子类的定义方式。具体来说,子类不再使用标准的 Python 字典作为其命名空间,而是使用字典子类在查找时自动定义名称(作为与名称相等的字符串)。

标签: python sly


【解决方案1】:

Python 知道four kinds of direct name lookupbuiltins / program global、模块全局、函数/闭包体和类体。 NAMENUMBER 在类体中解析,因此受制于这种范围的规则。

类主体在namespace provided by the metaclass 中进行评估,它可以为名称查找实现任意语义。具体来说,狡猾的Lexer 是一个使用a LexerMetaDict 作为命名空间的LexerMeta 类;此命名空间为未定义的名称创建新标记。

class LexerMetaDict(dict):
    ...
    def __getitem__(self, key):
        if key not in self and key.split('ignore_')[-1].isupper() and key[:1] != '_':
            return TokenStr(key, key, self.remap)
        else:
            return super().__getitem__(key)

LexerMeta 还负责adding the _ function to the namespace,这样就可以不用导入了。

class LexerMeta(type):
    '''
    Metaclass for collecting lexing rules
    '''
    @classmethod
    def __prepare__(meta, name, bases):
        d = LexerMetaDict()

        def _(pattern, *extra):
            ...

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

【讨论】:

    猜你喜欢
    • 2011-02-24
    • 2019-01-31
    • 2015-03-04
    • 1970-01-01
    • 2014-11-21
    • 2012-07-09
    • 2010-12-28
    • 1970-01-01
    相关资源
    最近更新 更多