什么时候可以在定义之前使用名称?

When can you use a name before it's defined?

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

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,这段代码执行得很好。这是如何运作的?什么时候可以引用尚未定义的名称?

Python 知道 four kinds of direct name lookup: builtins / program global、全局模块、function/closure 主体和 class 主体。 NAMENUMBER 在 class 主体中解析,因此受此类范围的规则约束。

class 主体在 namespace provided by the metaclass, which can implement arbitrary semantics for name lookups. In specific, the sly Lexer is a LexerMeta class using a LexerMetaDict 中被评估为命名空间;此命名空间为未定义的名称创建新标记。

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