为什么我可以在函数中使用一个变量,然后在Python中定义它? [英] Why can I use a variable in a function before it is defined in Python?

查看:207
本文介绍了为什么我可以在函数中使用一个变量,然后在Python中定义它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的代码中,我定义了两个函数。 主要立方体。我希望 main 成为我的程序的开始,所以我在 main中调用 cube

 >>> def main():
number = int(input('Enter a number:'))
cubed_number = cube(number)
print(立方体数:,立方体数)


>>> def cube(number):
return number * number * number

>>>

但是我在定义了 cube code> main ,所以我认为我的代码会引发 NameError 。然而,Python并没有抛出异常,而是完美地执行了我的代码:

 >>> main()
输入一个数字:5
立方数是:125
>>>

发生了什么?为什么Python能够运行我的代码,而不知道如何定义 cube 呢?为什么不是 NameError 被提升?



更奇怪的是,当我试图对类做同样的事情时,Python确实引发了一个 NameError

p>

 >>> class Main:
cubed_number()


Traceback(最近调用最后一个):
文件< pyshell#27>,第1行,位于<模块>
class Main:
文件< pyshell#27>,第2行,在主
cubed_number()
NameError:名称'cubed_number'未定义
>>>

发生了什么?




注意:这不是 ,因为那里的答案没有没有真正解释为什么或者这种行为在Python中如何工作。我创建了这个Q& A,因为目前这类问题的答案分散在各种问题中。我也觉得这将有助于展示幕后发生的事情。随时编辑和改进Q& A。为了理解发生了什么,我们必须理解Python与Python之间的区别定义函数,执行函数。

定义vs执行



当Python遇到函数定义时,它将 编译 函数到代码对象中。

代码对象是Python用来保存与特定可执行代码块关联的字节码的内部结构。它还包含Python需要执行字节码的其他信息,例如常量和局部变量名称。 文档提供了有关哪些代码对象更为全面的概述

然后使用代码对象构造一个函数对象。函数对象的代码对象随后用于在稍后调用该函数时执行该函数。 Python不会 执行 函数,它只会将函数编译为可以稍后用于执行的对象。 Python执行函数的唯一时间是调用函数

以下是相关部分提及此文档


函数定义是一个可执行语句。它的执行将当前局部命名空间中的函数名称绑定到函数对象(函数的可执行代码的包装器)。此函数对象包含对当前全局名称空间的引用,作为调用函数时要使用的全局名称空间。

函数定义不会执行函数身体;只有当函数被调用时才会执行。


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



这里是一个例子。我们定义一个函数 func ,它将两个变量加在一起; a b

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

正如你所看到的Python没有提出错误。这是因为它只是编译 func 。它没有试图执行这个函数,所以没有定义 a b



我们可以反汇编 func 的代码对象,并使用 dis 模块。这将告诉我们更多关于Python正在做什么的内容:

 >>> from dis import dis 
>>> dis(func)
2 0 LOAD_GLOBAL 0(a)
2 LOAD_GLOBAL 1(b)
4 BINARY_ADD
6 RETURN_VALUE

Python在字节码中编码了两个 LOAD_GLOBAL 指令。指令的参数分别是变量名 a b



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



让我们看看会发生什么当我们试图通过调用 func 来执行字节码时: >>> func()
Traceback(最近一次调用最后一次):
在< module>中,第1行的文件< pyshell#15>
func()
文件< pyshell#14>,第2行,在func
返回a + b
NameError:未定义名称'a'

正如你所看到的,Python引发了一个 NameError 。这是因为它试图执行两个 LOAD_GLOBAL 指令,但发现全局范围中未定义的名称。



现在让我们看看在调用之前定义两个变量 a b 会发生什么 func

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

上述原因是因为当Python执行 func 的字节码,它能够找到全局变量 a b ,并用它们来执行该函数。



这个例子同样适用于这个问题。编译 main 时,我们试图调用一个名为 cube 的变量,并生成一条指令以获取立方体的值。但是它没有试图找到一个名为 cube 的可调用对象,直到执行指令。在执行 main 的字节码时(例如调用 main ),函数名为 cube 已定义,所以Python不会引发错误。



如果我们试图在定义了cube,但是在上面的例子中,我们会因为相同的原因出现名称错误:

 > >> def main():
... number = int(input('Enter a number:'))
... cubed_number = cube(number)
... print(The number cubed is:,cubed_number)
...
>>> main()
输入一个数字:23
Traceback(最近一次调用的最后一个):
在< module>中,第1行的文件< pyshell#23>
main()
文件< pyshell#22>,第3行,位于主
cubed_number = cube(数字)
NameError:未定义名称'cube'



类怎么样?



Python句柄类定义与函数定义有点不同。



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



类定义的文档涉及这个



< blockquote>

然后使用新创建的本地名称空间和原始全局名称空间,在新的执行框架中执行类的套件(请参阅命名和绑定)。 (通常,套件主要包含函数定义。)当类的套件完成执行时,其执行框将被丢弃,但其本地名称空间已保存。




然而,这并不适用于方法。 Python与函数一样在方法中处理未定义的名称,并允许您在定义方法时使用它们:

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


In my code below, I am defining two functions. main and cube. I wanted main to be the start of my program, so I called cube inside of main:

>>> 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

>>> 

But I'm defining cube after main, so I thought my code would raise a NameError. However, instead of an exception being raised, Python executed my code perfectly fine:

>>> main()
Enter a number: 5
The number cubed is:  125
>>> 

What happened? Why was Python able to run my code, without knowing how cube was defined yet? Why wasn't a NameError raised?

Even stranger is that when I tried to do the samething with classes, Python did raise a 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
>>> 

What's happening here?


Note: This is not a duplicate of Why can I call a function before defining it, with only a warning?, because the answers there don't really explain why or how this behavior works in Python. I created this Q&A because the current answers for questions such as this are scattered across various questions. I also feel it would be beneficial to show what is happening behind the scenes for this to work. Feel free to edit and improve the Q&A.

解决方案

To understand what is happening, one must understand the distinction Python makes between defining a function, and executing a function.

Definition vs Execution

When Python encounters a function definition, it compiles the function into a code object.

A code object is an internal structure Python uses to hold the bytecode associated with a specific executable block of code. It also holds other information Python needs to execute the bytecode such as constants and local variable names. The documentation gives a much more more extensive overview of what code objects are.

The code object is then used to construct a function object. The function object's code object is then used to execute the function when it is later called. Python would not execute the function, it would only compile the function into an object that could be used later for execution. The only time Python executes a function, is when the function is called.

Here is the relevant part from 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.

Because of this distinction, Python cannot verify a name is actually defined until the function is called. Thus, you are allowed to use currently non-existent names in the function body. As long as the name is defined when the function is called, Python will not raise an error.

Here is an example. We define a function func that adds two variables together; a and b:

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

As you can see Python raised no error. This is because it simply compiled func. It did not attempt to execute the function, so it does not see that a and b are not defined.

We can disassemble func's code object, and see what the bytecode looks like using the dis module. This will tell us more about what Python is doing:

>>> from dis import dis
>>> dis(func)
  2           0 LOAD_GLOBAL              0 (a)
              2 LOAD_GLOBAL              1 (b)
              4 BINARY_ADD
              6 RETURN_VALUE

Python encoded two LOAD_GLOBAL instructions in the byte-code. The arguments to the instructions are the variable names a and b respectively.

This shows that Python did see that we were attempting to reference two variables when compiling our function and created bytecode instructions to do so. But it does not attempt to actually execute the instructions, until the function is called.

Let's see what happens when we attempt to execute the bytecode for func by calling it:

>>> 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

As you can see, Python raised a NameError. This is because it attempted to execute the two LOAD_GLOBAL instructions, but discovered that the names where undefined in the global scope.

Now let's see what happens if we defined two variables a and b before calling func:

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

The reason the above worked, is because when Python was executing func's byte-code, it was able to find the global variables a and b, and used those to execute the function.

The same applies to the example is the question. When main was compiled, Python "saw" we were trying to call a variable named cube and generated an instruction to get the value of cube. But it did not attempt to find a callable object named cube until the instructions were executed. And by the time main's byte-code was executed(eg. main was called), a function named cube was defined, so Python did not raise an error.

If we try to call main before cube is defined however, we'll get a name error for the same reasons in the above example:

>>> 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

What about the class?

Python handles class definitions a bit differently from function definitions.

When Python encounters a class definition, it creates a code object for the class as with the function. However, Python also allows classes to have namespaces that are executed during the class definition. Python does not wait to execute the classes namespace because any variables defined should belong to the class. Thus, any names used inside of a classes name-space must be defined for usage during class-definition time.

The 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.

However, this does not apply to methods. Python treats undefined names in methods as with functions, and allows you to use them when defining the method:

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

这篇关于为什么我可以在函数中使用一个变量,然后在Python中定义它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆