在下面的代码中,我定义了两个函数。main
和cube
。我希望main
作为程序的开始,所以我在main
内部调用了cube
:
>>> def main():
number = int(input('Enter a number: '))
cubed_number = cube(number)
print("The number cubed is: ", cubed_number)
>>> def cube(number):
return number * number * number
>>>
但是我在main
之后定义了cube
,所以我认为我的代码会引发一个NameError
。但是,Python没有引发异常,而是完美地执行了我的代码:
怎么了?为什么Python能够运行我的代码而不知道cube
是如何定义的?为什么没有提出?在
更奇怪的是,当我尝试对类进行同样的处理时,Python确实引发了一个NameError
:
>>> class Main:
cubed_number()
Traceback (most recent call last):
File "<pyshell#27>", line 1, in <module>
class Main:
File "<pyshell#27>", line 2, in Main
cubed_number()
NameError: name 'cubed_number' is not defined
>>>
这是怎么回事?在
注意:这并不是Why can I call a function before defining it, with only a warning?的重复,因为那里的答案并没有真正解释这种行为在Python中的工作原理。我之所以创建这个Q&A,是因为目前对这类问题的回答分散在各种问题中。我也觉得展示幕后发生的事情对这项工作是有益的。随时编辑和改进问答
要理解发生了什么,就必须理解Python在定义函数和执行函数之间的区别。在
定义与执行
当Python遇到函数定义时,它将函数编译成代码对象。在
code对象是Python用来保存与特定可执行代码块相关联的字节码的内部结构。它还保存了Python执行字节码所需的其他信息,例如常量和局部变量名。documentation gives a much more more extensive overview of what code objects are。在
然后使用code对象构造函数对象。函数对象的code对象随后用于在以后调用该函数时执行该函数。Python不会执行函数,它只会将函数编译成一个对象,以便以后执行。Python唯一执行函数的时间是调用函数时。在
以下是the documentation which mentions this中的相关部分:
由于这一区别,Python在调用函数之前无法验证名称是否已被实际定义。因此,允许您在函数体中使用当前不存在的名称。只要在调用函数时定义了名称,Python就不会引发错误。在
这里有一个例子。我们定义了一个函数}:
func
,它将两个变量相加;a
和{如您所见,Python没有出现错误。这是因为它只是编译了}没有定义。在
func
。它没有尝试执行函数,因此它看不到a
和{我们可以反汇编} 模块查看字节码的外观。这将告诉我们有关Python正在做什么的更多信息:
^{pr2}$func
的code对象,并使用^{Python用字节码编码了两条}。在
LOAD_GLOBAL
指令。指令的参数分别是变量名a
和{这表明Python确实看到我们在编译函数时试图引用两个变量,并为此创建了字节码指令。但它不会尝试实际执行指令,直到函数被调用。在
让我们看看当我们试图通过调用
func
执行字节码时会发生什么:如您所见,Python引发了一个
NameError
。这是因为它试图执行两条LOAD_GLOBAL
指令,但发现在全局范围内未定义名称。在现在让我们看看如果在调用},会发生什么:
func
之前定义了两个变量a
和{上述方法起作用的原因是,当Python执行},并使用它们来执行函数。在
func
的字节码时,它能够找到全局变量a
和{这个问题同样适用于这个例子。当
main
被编译时,Python“看到”我们试图调用一个名为cube
的变量,并生成一条指令来获取cube
的值。但是直到指令被执行,它才试图找到一个名为cube
的可调用对象。当main
的字节码被执行时(例如,main
被调用),一个名为cube
的函数被定义了,因此Python没有引发错误。在但是,如果我们在定义多维数据集之前尝试调用main,那么我们将得到一个名称错误,原因与上述示例中的相同:
那班呢?在
Python处理类定义与函数定义有点不同。在
当Python遇到类定义时,它会像函数一样为类创建一个code对象。但是,Python还允许类具有在类定义期间执行的名称空间。Python不会等待执行类名称空间,因为定义的任何变量都应该属于这个班。因此,在类名称空间内使用的任何名称都必须定义为在类定义期间使用。在
documentation for class definitions touches on this:
但是,这不适用于方法。Python将方法中未定义的名称视为函数,并允许您在定义方法时使用它们:
相关问题 更多 >
编程相关推荐