Приоритет операторов в Python
В Python возможно одновременное присваивание значений нескольким переменным:
a, b = 1, 2
a, b = b, a
И в то же время при объявлении функции допускается указание дефолтных значений:
def myfunc(a, b=0):
return a+b
Получается, что приоритет оператора "запятая" в первом случае выше, чем у оператора присваивания, а во втором случае оператор присваивания имеет более высокий приоритет? Как в действительности считается приоритет операторов в этом случае, и по возможности подскажите, как (предположительно) в Python реализуется такой переменный приоритет?
PS. Зачем мне это нужно: Я разрабатываю язык макросов для приложения (для этого, чтобы не изобретать велосипед, беру примеры в других языках), и хочу внедрить массовое присваивание и параметры по умолчанию. Для этого мне нужно определиться с приоритетом операторов, и как-то решить эту проблему: не хотелось бы хардкодить, задавать переменный приоритет для запятой, проверяя, в скобках ли она или нет, что усложнит код парсера и сделает его менее удобным для дальнейшего расширения.
Ответы (2 шт):
Запятая не является оператором в Python (т.е. неверно поставлен вопрос изначально - не идёт речи о приоритете операторов). Она - просто синтаксический разделитель в операции присваивания. Иными словами, компилятор Python преобразует согласно синтаксису языка выражение
a, b = 1, 2
в (см.ниже в комментарии полезное исправление от @CrazyElf !)
a=1
b=2
Аналогично с
a, b = b, a
Это просто сокращённая запись:
temp = a
a = b
b = temp
Полагаю, правильно говорить, что изначально компилятор проводит синтаксически определённые изменения, и только после этого переходит к вопросу приоритетов.
Уточню, что согласно документации* на вход парсера подаётся поток токенов, так что преобразования эти должны проводится им. Сам же поток токенов рассматриваемое преобразование не содержит. Для наглядности пример с результатом токенизации для
a, b = 1, 2
приведён ниже.
TokenInfo(type=63 (ENCODING), string='utf-8', start=(0, 0), end=(0, 0), line='')
TokenInfo(type=1 (NAME), string='a', start=(1, 0), end=(1, 1), line='a,b=1,2\r\n')
TokenInfo(type=54 (OP), string=',', start=(1, 1), end=(1, 2), line='a,b=1,2\r\n')
TokenInfo(type=1 (NAME), string='b', start=(1, 2), end=(1, 3), line='a,b=1,2\r\n')
TokenInfo(type=54 (OP), string='=', start=(1, 3), end=(1, 4), line='a,b=1,2\r\n')
TokenInfo(type=2 (NUMBER), string='1', start=(1, 4), end=(1, 5), line='a,b=1,2\r\n')
TokenInfo(type=54 (OP), string=',', start=(1, 5), end=(1, 6), line='a,b=1,2\r\n')
TokenInfo(type=2 (NUMBER), string='2', start=(1, 6), end=(1, 7), line='a,b=1,2\r\n')
TokenInfo(type=4 (NEWLINE), string='\r\n', start=(1, 7), end=(1, 9), line='a,b=1,2\r\n')
TokenInfo(type=0 (ENDMARKER), string='', start=(2, 0), end=(2, 0), line='')
Если же интересует непосредственно построение AST-дерева, то удобно воспользоваться стандартным модулем ast. Результат для нашего примера (можем видеть, что @CrazyElf был совершенно прав по поводу кортежей):
Module(body=[Assign(targets=[Tuple(elts=[Name(id='a', ctx=Store()), Name(id='b', ctx=Store())], ctx=Store())], value=Tuple(elts=[Constant(value=1), Constant(value=2)], ctx=Load()))], type_ignores=[])
Вот интересная ссылка по поводу работы с AST и байт-кодом: https://habr.com/ru/company/piter/blog/493424/
А вот и код для токенизации и построения ast-дерева:
import ast
import tokenize
print("Токены:")
with open ('temp.py', 'rb') as f: #файл содержит строку a,b=1,2
for token in tokenize.tokenize(f.readline):
print(token)
my_tree=ast.parse('a,b=1,2',)
print('\nAST-дерево:\n', ast.dump(my_tree))
*первая строка в https://python.readthedocs.io/en/stable/reference/lexical_analysis.html#index-0
Я принял решение, в своём языке макросов, для массового присвоения значений переменным использовать "=":
a, b, c = 1, 2, 3;
А для значения параметра по умолчанию - паскалевский оператор ":=", присвоив ему высший приоритет:
myfunc = a, b:=0 => a + b;
Несмотря на то, что я сам отвечаю, правильный ответ я ставлю пользователю Сергей, как более полный, информативный и практически полезный.