Static and Runtime Name Resolution
这篇文章讲 python 静态时期和动态运行时对名称的解析,这包含了我对静态时期和动态运行时的理解。
- 静态时期:静态时期仅仅指编写代码的这个时候,就是编写代码时候能够看到的东西。
- 动态运行时:动态运行时指程序运行到这行代码时候能看到的东西。
很多东西工作在静态时期,比如函数的注解,其完全在静态时期起作用。也有很多东西工作在动态时期,比如过函数的代码,类对象的创建,类实例的创建。以下举出几个例子来帮助理解。
定义函数时
python
def funcA():
print("A")
funcB()
def funcB():
print("B")
funcA()
这段代码是可以通过 python 执行的,funcA
在 funcB
还没定义的时候就在代码中,就出现在 funcA
的代码中的,这是没关系的,因为在定义的时候只进行了对于函数名称的绑定,即将定于的函数绑定到函数的名称,但是对于函数的代码却不会进行运行。函数的代码被编译到字节码之后只知道在运行时候要去找 funcB
,而不是在静态的时候去找 funcB
,只要在运行的时候能找到 funcB
这个名称就行了。
python
def funcA():
print("A")
funcB()
即使是这样,这个定义也是正确的。
函数或者方法注解
方法注解完全是在静态时期工作的,因此它使用到的名称需要在静态的时候就能看到。
python
def funcB(a: A):
pass
这段代码会报错,因为他在静态时候看不到A
的定义。
python
class A:
pass
def funcB(a: A):
pass
这段代码不会报错,静态的时候按照定义的初始化的顺序,类 A
的初始化在 funcB
之前,因此在静态的时候 funcB
能看到 A
。
python
def funcB(a: A):
pass
class A:
pass
加入换个顺序,就会报错的,因为这个时候 funcB
看不到 A
了。因此要使用 python 的前向声明,也就是将 A
用引号引起来,告诉 python 这个名称会在后面定义。
python
def funcB(a: 'A'):
pass
class A:
pass
这样就不会出问题。
类创建时和类实例创建时
类创建时和类实例创建的时候都是动态运行时,因此所用的名称一定要在运行的时候能看到。
python
class B:
b = B
这是错误的因为 b
的赋值操作 B
类对象创建的时候进行,这时候 B
的名称还不能被 B
内部看见,因此会报错。
python
class B:
def func():
self.b1 = B
这是正确的,类中方法的执行在该方法被调用时,在调用这个方法的时候类对象已经被创建,因此在这个时候是能看到 B
这个名称的。