Спецификация грамматики Python 3.2 - вызов функции - PullRequest
1 голос
/ 03 апреля 2012

Это относится к Python 3.2.Соответствующие правила грамматики следующие (http://docs.python.org/py3k/reference/grammar.html):

power: atom trailer* ['**' factor]
atom: ('(' [yield_expr|testlist_comp] ')' |
       '[' [testlist_comp] ']' |
       '{' [dictorsetmaker] '}' |
       NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False')
trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME

В соответствии с этим основной вызов функции может выглядеть следующим образом:

atom '(' ')'

Но я считаю, что мы можемпросто поместите туда any * 1012. * Например, даже если 1 является atom (NUMBER), 1 не является функцией, и поэтому вы не можете вызывать ее с помощьючто-то вроде 1(). Мой вопрос: учитывая экземпляр правила грамматики power, может ли его atom быть заменено любым правилом atom, отличным от NAME в дереве разбора программы Pythonи все еще выполняется?

РЕДАКТИРОВАТЬ (ОТВЕТ):

Все, что «вызывается», может быть вызвано с оператором (). (http://docs.python.org/py3k/reference/expressions.html#calls):

call ::=  primary "(" [argument_list [","] | comprehension] ")"

primary должен вычисляться как вызываемый объект (пользовательские функции, встроенные функции, методы встроенных объектов, объекты классов, методы экземпляров классов и все объекты, имеющие__call__() метод вызывается).

Это означает, что вы можете делать такие вещи, как:

>>> eval.__call__.__call__("print(\"x\")")
x

Или даже cРазные (бесполезные) вещи:

>>> a = lambda x : [abs, lambda y : y][0 if x < 0 else 1](x)
>>> a(1)
1
>>> a(-1)
1
>>> a(0)
0

Ответы [ 2 ]

7 голосов
/ 03 апреля 2012
>>> 1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>>

Обратите внимание, что здесь ошибка TypeError, а не SyntaxError.Совершенно законно синтаксис пытаться позвонить по номеру;номера просто не имеют фактического вызова функциональность .

0 голосов
/ 03 апреля 2012

Подвыражение в скобках также может быть вызвано, поскольку оно может быть функцией, даже не содержащей имени: (lambda: 1)(). Глядя на грамматику, я не могу точно сказать, как именно она анализируется (кажется, что это не yield_expr или testlist_comp, но я не вижу ничего, кроме atom, которое можно использовать вызовите скобки после любого из них).

Несмотря на это, парсер не выполняет принудительные семантические проверки. 1() вполне может интерпретироваться как попытка вызвать номер 1 без аргументов; означает ли это что-нибудь или нет, зависит от переводчика. Сбои синтаксического анализатора относятся к случаям, когда интерпретатор не может выяснить, какие операции запрашивает код, даже пытаясь их выполнить.

Выделение NAME как единственного законного трейлера вызова функций добавило бы ненужную сложность к грамматике для небольшой реальной выгоды. Python уже имеет достаточно хороший механизм для сообщения об ошибке при попытке вызвать что-то, что не может быть вызвано, и этот механизм является исключением во время выполнения. Исключение во время выполнения всегда должно было бы существовать, потому что существует множество имен, связанных с не вызываемыми объектами, поэтому использование концептуального механизма везде менее затратно.

Это было бы немного нелогично и на уровне программиста на Python. В моем внутреннем мышлении о синтаксисе Python есть другие вещи, которые не являются синтаксически именами, которые вполне разумно называть, например, следующее:

result()()
dict_of_funcs[name]()

Просто так получилось, что ни один из них не был разобран как atom (потому что они скорее разбираются как один atom, за которым следуют несколько trailer с), но я не думаю насчет языка Python, я просто думаю, что «вы можете вызывать все, что является выражением» (правила старшинства по модулю, которые могут потребовать добавления скобок), и при таком понимании я был бы удивлен, если не смог вызвать целое число или строку ( тем более что я могу, когда это имя, а не буквальное).

Это также усложнит изменение семантики языка. Представьте себе гипотетическое будущее изменение языка, в котором вызов словаря определен так же, как поиск ключа, чтобы я мог написать {1: 'foo'}(1). В соответствии с текущей грамматикой все, что потребуется, это реализовать эквивалент __call__ для встроенного типа dict. Если бы только NAME и не другие атомы могли иметь трейлер '(' [arglist] ')', то это привело бы к несоответствию, в котором нельзя было бы вызвать буквальное значение dict, но можно было бы назвать имя, связанное со значением dict. Несоответствие, похожее на это, уже существует, когда x.__class__ хорошо, а 1.__class__ нет, но это просто компромисс из-за существования литералов с плавающей запятой, таких как 1.0. Нет аналогичного случая сделать 1() недопустимым синтаксисом, а не просто вызывать исключение.

...