Доступ к атрибутам по int литералам

>>> 1 .__hash__()
1
>>> 1.__hash__()
  File "<stdin>", line 1
    1.__hash__()
             ^
SyntaxError: invalid syntax

Здесь было рассмотрено, что второй пример не работает, потому что int literal фактически анализируется как float.

Мой вопрос: почему python не анализирует это как доступ к атрибуту в int, когда интерпретация как float является синтаксической ошибкой? В разделе docs на лексическом анализе кажется, что пробел требуется только тогда, когда другие интерпретации неоднозначны, но, возможно, я читаю этот раздел неправильно.

По подозрению в том, что lexer жадный (пытаясь взять самый большой токен), но у меня нет источника для этого требования.

Ответ 1

Прочитайте внимательно, он говорит

Пробел между двумя токенами требуется только в том случае, если их конкатенация иначе интерпретируется как другой токен (например, ab - это один токен, а b - два токена).

1.__hash__() обозначается как:

import io, tokenize
for token in tokenize.tokenize(io.BytesIO(b"1.__hash__()").read):
    print(token.string)

#>>> utf-8
#>>> 1.
#>>> __hash__
#>>> (
#>>> )
#>>>

Python lexer выберет токен который содержит самую длинную возможную строку, которая формирует легальный токен при чтении слева направо; после разбора два токена должны быть объединены в действительный токен. Логика очень похожа на в вашем другом вопросе.

Путаница, по-видимому, не признает шаг токенизации как совершенно отличный шаг. Если грамматика позволила разделить токены исключительно для того, чтобы сделать синтаксический анализатор счастливым, вы наверняка ожидаете

_ or1.

tokenize как

_
or
1.

но такого правила нет, поэтому он символизирует как

_
or1
. 

Ответ 2

Лексер очень прост и не будет отступать. Языковые парсеры часто делятся на фазу лексинга и фазу синтаксического анализа, или лексер, и парсер. Лексер разбивает поток символов на токены, а затем парсер определяет структуру программы из токенов. Лексер видит четыре токена: 1., __hash__, (, ): float, идентификатор, open-paren, close-paren. Парсер не может понять эти жетоны, но это не означает, что lexer будет пытаться использовать символы lex по-разному.

Ответ 3

Это просто вопрос определения; для языков грамматика выполняет задание.

Ссылки атрибутов определены на гораздо более широком уровне, чем литеры с плавающей запятой. Таким образом, с уровня грамматики анализатор должен распознавать 1. как литерал с плавающей запятой, а не как ссылку на атрибут.

Разумеется, сам анализатор мог вернуться к достижению _ и попытаться выяснить, что вместо него это не буква с плавающей запятой, а ссылка на атрибут. Однако, поскольку парсинг CPythons является LL (1) парсер, то возврат не является вариантом. Таким образом, грамматику нужно будет значительно изменить, чтобы позволить синтаксическому анализатору распознать это (хотя Im не уверен прямо сейчас, если это возможно даже с парсером LL (1)). Мы могли бы также сменить парсер Pythons на что-то еще, возможно, на то, что делает откат, но это не только очень сложная задача (для этого также потребуется изменить грамматику), но и значительно увеличит сложность процесса синтаксического анализа (и с вероятно, уменьшит скорость).

Так что, возможно, это было бы возможно, но это потребует серьезных изменений в спецификации языка. И это само по себе было бы проблематичным. Он также нарушит существующий код, который использует это раннее распознавание поплавка, например. 1.if True else 0.