Skip to content

Static and Runtime Name Resolution

这篇文章讲 python 静态时期和动态运行时对名称的解析,这包含了我对静态时期和动态运行时的理解。

  • 静态时期:静态时期仅仅指编写代码的这个时候,就是编写代码时候能够看到的东西。
  • 动态运行时:动态运行时指程序运行到这行代码时候能看到的东西。

很多东西工作在静态时期,比如函数的注解,其完全在静态时期起作用。也有很多东西工作在动态时期,比如过函数的代码,类对象的创建,类实例的创建。以下举出几个例子来帮助理解。

定义函数时

python
def funcA():
    print("A")
    funcB()
    
def funcB():
    print("B")
    funcA()

这段代码是可以通过 python 执行的,funcAfuncB 还没定义的时候就在代码中,就出现在 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 这个名称的。