Я недавно тоже создал токенизатор и решил некоторые ваши проблемы.
Типы токенов объявляются как «константы», то есть переменные с именами ALL_CAPS, на уровне модуля. Например,
_INTEGER = 0x0007
_FLOAT = 0x0008
_VARIABLE = 0x0009
и так далее. Я использовал подчеркивание перед именем, чтобы указать, что каким-то образом эти поля являются «частными» для модуля, но я действительно не знаю, является ли это типичным или желательным, даже не настолько, насколько Pythonic. (Кроме того, я, вероятно, буду отбрасывать числа в пользу строк, потому что во время отладки они намного более читабельны.)
Жетоны возвращаются как именованные кортежи.
from collections import namedtuple
Token = namedtuple('Token', ['value', 'type'])
# so that e.g. somewhere in a function/method I can write...
t = Token(n, _INTEGER)
# ...and return it properly
Я использовал именованные кортежи, потому что клиентский код токенизатора (например, анализатор) кажется немного более понятным при использовании имен (например, token.value) вместо индексов (например, token [0]).
Наконец, я заметил, что иногда, особенно при написании тестов, я предпочитаю передавать строку токенизатору вместо файлового объекта. Я называю это «читатель», и у меня есть специальный метод, чтобы открыть его и позволить токенизатору получить доступ к нему через тот же интерфейс.
def open_reader(self, source):
"""
Produces a file object from source.
The source can be either a file object already, or a string.
"""
if hasattr(source, 'read'):
return source
else:
from io import StringIO
return StringIO(source)