【问题标题】:Python 3.2 Grammar Specification - Function CallPython 3.2 语​​法规范 - 函数调用
【发布时间】:2012-04-03 04:19:32
【问题描述】:

这是对 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 atom 放在那里。例如,即使1atom (NUMBER),1 也不是函数,因此您不能使用1() 之类的名称调用它。我的问题是:给定一个power 语法规则的实例,它的atom 是否可以在Python 程序的解析树中被atom 的任何规则替换而不是NAME 并且仍然运行?

编辑(回答):

任何“可调用”的东西都可以用() 操作符调用。 (http://docs.python.org/py3k/reference/expressions.html#calls):

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

primary 必须评估为可调用对象(用户定义的函数、内置函数、内置对象的方法、类对象、类实例的方法,以及所有具有 __call__() 方法的对象都是可调用的)。

这意味着您可以执行以下操作:

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

甚至更疯狂(无用)的东西:

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

【问题讨论】:

  • 简单的答案是“是”。您需要设置更多限制。
  • 您的“EDIT(ANSWER)”示例实际上与您的问题没有太大关系;它们都没有被解析为atom,后跟调用括号。像[func][0]() 这样的东西是atom [func] 后跟一个预告片[0] 后跟另一个预告片()
  • @Ben,是的,我现在看到这些示例实际上是 trailers 而不是 atoms。 EDIT(ANSWER) 适用于一个稍微不同的问题,我在进行编辑之前在评论中提出了这个问题(“我想知道除了函数名称之外可以在函数调用之前立即放置哪些类型的东西,并且有它实际上运行而不仅仅是解析。”)。我是社区的新手,明白你的意思。就编辑我的问题或保持不变而言,此时应该做什么?

标签: python compiler-construction grammar


【解决方案1】:
>>> 1()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
>>>

注意这里的错误是TypeError 而不是SyntaxError。尝试拨打号码是完全合法的语法;数字只是没有任何实际调用功能

【讨论】:

  • +1。仅仅因为某些东西在语法中是有效的,并不意味着它有意义或者会起作用——它只是意味着解析器不会抱怨。
  • 对。好吧,根据语法规则,放一个数字就可以了。我想知道除了函数名称之外,可以在函数调用之前立即放置哪些类型的东西,并让它实际运行而不仅仅是解析。
  • @Words:任何具有__call__() 方法的东西。问题是大多数atoms 没有。
【解决方案2】:

带括号的子表达式也是可调用的,因为它可能是一个函数,即使不包含名称:(lambda: 1)()。查看语法,我实际上无法确切地知道它是如何解析的(它似乎既不是yield_expr 也不是testlist_comp,但除了atom 之外我看不到其他任何东西都可以坚持后面的调用括号)。

无论如何,执行语义检查并不是解析器的工作。 1() 完全可以解释为尝试不带参数地调用数字 1;这是否意味着什么取决于口译员。解析器失败是因为解释器无法弄清楚代码请求什么操作,甚至无法尝试执行它们。

NAME 作为唯一合法的函数调用预告片会增加语法的不必要的复杂性,而没有什么真正的好处。 Python已经有一个非常好的机制来报告试图调用无法调用的东西的错误,并且该机制是运行时异常。运行时异常总是必须存在的,因为有很多名称绑定到不可调用的东西上,所以在任何地方使用相同的机制只会减少概念上的开销。

这在 Python 程序员的水平上也有点违反直觉。在我对 Python 语法的内化思考方式中,还有一些在语法上不是完全合理的名称的东西,例如:

result()()
dict_of_funcs[name]()

碰巧这些都没有被解析为atom(因为它们被解析为单个atom,后跟多个trailers)但我不是这样认为 em> 关于 Python 语言,我只是认为“你可以调用任何表达式”(可能需要添加括号的模优先规则),在这种理解下,我会惊讶于无法调用整数或字符串(尤其是当它是一个名字而不是文字时我可以)。

这也会使更改语言语义变得更加困难。想象一下未来对语言的假设更改,其中调用字典被定义为与查找键相同,这样我就可以写{1: 'foo'}(1)。在当前语法下,只需为内置的dict 类型实现__call__ 的等价物。如果只有NAME 而其他原子不能有'(' [arglist] ')' 预告片,那么这将引入一个不一致性,其中无法调用文字dict 值,但可以调用绑定到dict 值的名称。在x.__class__ 很好但1.__class__ 不是的地方已经存在类似的不一致性,但这基本上只是由于存在像1.0 这样的浮点文字而导致的妥协。没有类似的情况使1() 语法无效,而不仅仅是引发异常的操作。

【讨论】:

    猜你喜欢
    • 2022-12-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-20
    • 2018-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多