Python:名称解析;函数def的顺序

时间:2011-02-08 19:42:05

标签: python function namespaces

我有一个非常简单的例子:

#!/usr/bin/env python

#a()  # 1: NameError: name 'a' is not defined
#b()  # 1: NameError: name 'b' is not defined
#c()  # 1: NameError: name 'c' is not defined

def a():
    c()   # note the forward use here...

#a()  #2: NameError: global name 'c' is not defined 
#b()  #2: NameError: name 'b' is not defined
#c()  #2: NameError: name 'c' is not defined

def b():
    a()

#a()   #3: NameError: global name 'c' is not defined    
#b()   #3: NameError: global name 'c' is not defined
#c()   #3: NameError: name 'c' is not defined

def c():
    pass

a()    # these all work OK...   
b()
c()

我在Python源文件中按字母顺序定义了3个名为a()b()c()的函数。每个函数定义的主体是对其他函数之一的调用。您可以通过我的注释看到我必须首先调用这些函数中的第一个函数(在文本文件中),但是您不一定需要在调用它的另一个函数上面定义函数。

当然,通常的做法是将第一个可执行代码放在所有函数定义(Python和许多其他语言)之下,现在我可以看到原因。在C和C ++中,头文件处理这个问题。在Pascal中,您必须在使用之前使用名称定义。

例如,假设你在Python中有这个:

def a(a_arg):          c(a_arg)
def b(b_arg):          a()
def c(a_arg,b_arg):    b(b_arg)
a(1)

在运行时,TypeError: c() takes exactly 2 arguments (1 given)将无法正常运行,其他错误是编译时。 (在C中,这将编译然后神秘地失败...)

在Perl中,由于子例程名称在运行时是USUALLY解析的,因此您可以按任何顺序使用Perl定义和代码:

#!/usr/bin/env perl

a();
b();
c();

sub a{ c(); }
sub b{ a(); }
sub c{ return; }

在C中,使用一个尚未原型化且不应忽略的函数是错误或警告(依赖于实现)。

你可以拥有:

void a(void) { c(); }   /* implicitly assumed to be int c(...) unless prototyped */
void b(void) { a(); }
void c(void) { return; }

int main(void) {
    a();
    return EXIT_SUCCESS;
}

我的假设和困惑是:如果Python直到运行时才解析子程序名称,为什么源编译阶段失败并且尚未定义子程序名称的前向声明?是否在某个地方(除了观察其他代码之外)记录了在子程序定义之上的源文件中没有代码?

似乎Python有dynamic name resolution的元素(在c()中使用a()之前在源文件中定义它)和静态名称解析的元素(失败的Python如果放在源文件中的定义之上,则运行对a()的调用。)

是否有THIS DOCUMENT的Python版本涵盖了Perl可执行文件的生命周期以及如何在源文件解释和运行时之间解析名称?

在Python脚本的定义顺序上是否存在明确的描述,该脚本声明函数可以具有其他子例程名称的前向定义,但主代码不能?

编辑和结论

经过一些精彩的评论和我的一些研究之后,我得出结论,我的问题更多的是如何解析名称,以及如何在Python中定义名称空间,范围和模块。

来自carot-top

  

“必须在当前命名空间中调用callable之前定义它。”   和this link关于范围和名称

来自S.Lott

  

“当在代码块中使用名称时,使用最近的封闭范围解析它。”   和this link到Python脚本的执行期。

来自Python文档:

  

“范围定义了块内名称的可见性。”来自Python Execution model

     

“模块可以包含可执行语句以及函数定义。”在more about modules

     

“实际上,函数定义也是'执行'的'语句';模块级函数的执行在模块的全局符号表中输入函数名。”在其脚注中。

我自己的认识(呃!):

  1. Python将每个Python源文件视为“模块”:“模块是包含Python定义和语句的文件。”

  2. 与Perl(我有更多经验)不同,Python在读取模块时执行模块。因此,直接可执行语句的失败引用了尚未在同一模块中定义的函数。

5 个答案:

答案 0 :(得分:25)

定义的顺序只是“在你调用它之前必须定义所有内容”。这就是它。

编辑(在评论中包含答案,已阐明):

类似

的原因
def call_a():
    a()

def a():
    pass

call_a()

a() call_a() a之前call_a工作,a()甚至被定义为函数是因为Python实际上只查找上符号的值基础。在评估a时,call_a()调用基本上存储为字节码指令,以“查找call_a是什么,并在时机到来时将其称为 ,直到你深入到底部Disassembly of call_a: 2 0 LOAD_GLOBAL 0 (a) 3 CALL_FUNCTION 0 6 POP_TOP 7 LOAD_CONST 0 (None) 10 RETURN_VALUE 的实际调用。

以下是call_a的反汇编字节码(通过dis.dis):

a

所以基本上,当你点击None时,它会将存储为call_a() is None的任何内容加载到堆栈中,将其作为函数调用,然后在返回True之前弹出返回值,对于任何未明确返回的内容({{1}}返回{{1}})隐式发生的事情

答案 1 :(得分:5)

遵循各种评论并努力理解Python历史中的一些Perl概念,让我对此进行分析。关于你在Perl中学到的一些事情,请reset your brain。它们不适用于Python。 (和vs vs ...)

Python中的

There are没有forward declarations。没有。从技术上讲,所有函数都是匿名对象;它们恰好与您用来定义它的名称绑定。你可以随意重新绑定它们。

可以使用locals()函数找到这些函数的字典,如下所示:

>>> def a(): b()
... 
>>> locals()['a']
<function a at 0x100480e60>
>>> locals()['b']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'b'
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in a
NameError: global name 'b' is not defined

如果Python在编写b()之前需要定义a(),那么这将是Python解释器中的一个问题。您需要严格按顺序编写所有函数。

由于所有内置函数名称都只是有界名称,因此您可以轻松覆盖内置函数:

>>> abs(-1)
1
>>> def abs(num): print "HA Fooled you!!!"
... 
>>> abs(-1)
HA Fooled you!!!
>>> abs=__builtins__.abs
>>> abs(-1)
1

在Perl中覆盖内置函数要困难得多(但可能)。 (这里的缺点是错误的def [builtin]:可能无意中覆盖内置函数而没有警告)

我可以在Python中引用您的名称和范围的最佳描述实际上是Classes -- section 9.2上的教程

在可执行代码之前,为什么def需要实际上没有章节和经文,因为这不是一个真实的陈述。考虑:

#!/usr/bin/env python

def fake_a(): print " a fake"
a=fake_a
a()  
def a():  print "this is a()"
a()

甚至:

def a(): print " first a()"
a()  
def a():  print "second a()"
a()

真实的是,必须在当前命名空间中调用之前定义一个callable。因此,通常,高于源文本文件或模块中的可执行代码调用的点。每个函数都有自己的命名空间;当在函数的命名空间中调用callable时,在该本地和正在执行的命名空间中调用该函数时,对尚未定义的其他函数的调用仅失败。这就是为什么你可以在你的例子中看起来像一个“前向声明”。函数外部的“前向”可调用模块调用失败,因为函数def尚未执行,因此它不在当前名称空间中。

答案 2 :(得分:2)

这与C / C ++等完全相同。在它存在之前你不能使用它。在C / C ++中,在声明之前你不能引用它。请记住,Python文件是从上到下处理的,因此如果您尝试调用函数或引用不存在的变量,那么它将失败。

答案 3 :(得分:2)

http://docs.python.org/reference/executionmodel.html


  

在代码块中使用名称时,   它使用最近的解决方案   封闭范围。这一切都是这样的   代码块可见的范围是   称为区块的环境。

     

以下构造绑定名称:   函数的形式参数,导入   陈述,类和功能   定义(这些绑定类或   定义块中的函数名称),   ...

就是这样。没有“前进”或“反向”或“声明”的概念或类似的东西。

答案 4 :(得分:0)

我知道这个问题现在已经回答了很长一段时间,但我也很难理解这个问题,并希望通过一个简单的例子来证明我是如何找到答案的:

def a():
     print globals()
     b()
a()

在上面的示例中,如果主要可执行代码定义了名为b的函数,则globals将显示使用名称b定义的函数的位置。如果不是,全局变量将为空并且调用a将导致

NameError: global name 'b' is not defined  
例如在狼的例子中。这意味着虽然函数名称需要在它们被调用之前声明它们存在于主执行代码的命名空间中,但是主执行代码调用的函数已经具有在main中找到的定义,这就是函数调用的原因。功能不需要遵循严格的命令。