什么时候可以在定义之前使用名称?
在 SLY 中有一个编写计算器的例子(从calc.py 这里复制):
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
看起来它被窃听了,因为NAME和NUMBER在它们被定义之前就被使用了。但实际上,没有NameError,并且这段代码执行得很好。这是如何运作的?什么时候可以在定义之前引用名称?
回答
Python 知道四种直接名称查找:内置程序/程序全局、模块全局、函数/闭包体和类体。的NAME,NUMBER决心在一个类体,因此受这种范围的规则。
类主体在 metaclass 提供的命名空间中进行评估,它可以为名称查找实现任意语义。具体来说,slyLexer是一个LexerMeta使用aLexerMetaDict作为命名空间的类;此命名空间为未定义的名称创建新标记。
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还负责添加_功能的命名空间,以便它可以不用进口来使用。
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