为什么在Python中定义变量之前我可以在函数中使用它?

2024-04-26 06:18:12 发布

您现在位置:Python中文网/ 问答频道 /正文

在下面的代码中,我定义了两个函数。maincube。我希望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没有引发异常,而是完美地执行了我的代码:

^{pr2}$

怎么了?为什么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,是因为目前对这类问题的回答分散在各种问题中。我也觉得展示幕后发生的事情对这项工作是有益的。随时编辑和改进问答


Tags: 代码number定义ismaindeflinecall
1条回答
网友
1楼 · 发布于 2024-04-26 06:18:12

要理解发生了什么,就必须理解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中的相关部分:

A function definition is an executable statement. Its execution binds the function name in the current local namespace to a function object (a wrapper around the executable code for the function). This function object contains a reference to the current global namespace as the global namespace to be used when the function is called.

The function definition does not execute the function body; this gets executed only when the function is called.

由于这一区别,Python在调用函数之前无法验证名称是否已被实际定义。因此,允许您在函数体中使用当前不存在的名称。只要在调用函数时定义了名称,Python就不会引发错误。在

这里有一个例子。我们定义了一个函数func,它将两个变量相加;a和{}:

>>> def func():
...     return a + b

如您所见,Python没有出现错误。这是因为它只是编译了func。它没有尝试执行函数,因此它看不到a和{}没有定义。在

我们可以反汇编func的code对象,并使用^{}模块查看字节码的外观。这将告诉我们有关Python正在做什么的更多信息:

^{pr2}$

Python用字节码编码了两条LOAD_GLOBAL指令。指令的参数分别是变量名a和{}。在

这表明Python确实看到我们在编译函数时试图引用两个变量,并为此创建了字节码指令。但它不会尝试实际执行指令,直到函数被调用。在

让我们看看当我们试图通过调用func执行字节码时会发生什么:

>>> func()
Traceback (most recent call last):
  File "<pyshell#15>", line 1, in <module>
    func()
  File "<pyshell#14>", line 2, in func
    return a + b
NameError: name 'a' is not defined

如您所见,Python引发了一个NameError。这是因为它试图执行两条LOAD_GLOBAL指令,但发现在全局范围内未定义名称。在

现在让我们看看如果在调用func之前定义了两个变量a和{},会发生什么:

>>> a = 1
>>> b = 2
>>> 
>>> func()
3

上述方法起作用的原因是,当Python执行func的字节码时,它能够找到全局变量a和{},并使用它们来执行函数。在

这个问题同样适用于这个例子。当main被编译时,Python“看到”我们试图调用一个名为cube的变量,并生成一条指令来获取cube的值。但是直到指令被执行,它才试图找到一个名为cube的可调用对象。当main的字节码被执行时(例如,main被调用),一个名为cube的函数被定义了,因此Python没有引发错误。在

但是,如果我们在定义多维数据集之前尝试调用main,那么我们将得到一个名称错误,原因与上述示例中的相同:

>>> def main():
...     number = int(input('Enter a number: '))
...     cubed_number =  cube(number)
...     print("The number cubed is: ", cubed_number)
... 
>>> main()
Enter a number: 23
Traceback (most recent call last):
  File "<pyshell#23>", line 1, in <module>
    main()
  File "<pyshell#22>", line 3, in main
    cubed_number =  cube(number)
NameError: name 'cube' is not defined

那班呢?在

Python处理类定义与函数定义有点不同。在

当Python遇到类定义时,它会像函数一样为类创建一个code对象。但是,Python还允许类具有在类定义期间执行的名称空间。Python不会等待执行类名称空间,因为定义的任何变量都应该属于这个班。因此,在类名称空间内使用的任何名称都必须定义为在类定义期间使用。在

documentation for class definitions touches on this

The class’s suite is then executed in a new execution frame (see Naming and binding), using a newly created local namespace and the original global namespace. (Usually, the suite contains mostly function definitions.) When the class’s suite finishes execution, its execution frame is discarded but its local namespace is saved.

但是,这不适用于方法。Python将方法中未定义的名称视为函数,并允许您在定义方法时使用它们:

>>> class Class:
...     def method(self):
...         return var
...
>>> var = 10
>>> cls = Class()
>>> cls.method()
10

相关问题 更多 >