在 Python 中,import 语句会执行一个文件中的顶层代码,然后将各种函数定义体预先编译成字节码(Python 中字节码可以通过 dis 反编译模块看到)缓存起来,以后重新导入相同的模块就使用缓存,只做名称绑定。对于函数来说,Python 解释器会将 def 定义的函数体编译成字节码,并且将函数对象绑定到对应的全局名称上,但是这时候显然不会执行定义体,解释器只会在运行时调用函数的时候才会执行定义体。

但是对于类来说,导入的时候,解释器会执行类的定义体,类中嵌套的类定义体也会被执行。

下面通过代码来理解导入时和运行时的差别,建议先不要执行代码,先通过纸笔模拟一遍执行过程:

假设 evaltime.py 通过两种方式调用:

  1. 通过在 Python 交互式解释器导入运行:
1
import evaltime
  1. 通过命令行调用解释器解释脚本:
1
python evaltime.py

输出的结果分别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# evaltime.py
from evalsupport import deco_alpha

print('<[1]> evaltime module start')

class ClassOne():
print('<[2]> ClassOne body')

def __init__(self):
print('<[3]> ClassOne.__init__')

def __del__(self):
print('<[4]> ClassOne.__del__')

def method_x(self):
print('<[5]> ClassOne.method_x')

class ClassTwo(object):
print('<[6]> ClassTwo body')

@deco_alpha
class ClassThree():
print('<[7]> ClassThree body')

def method_y(self):
print('<[8]> ClassThree.method_y')

class ClassFour(ClassThree):
print('<[9]> ClassFour body')

def method_y(self):
print('<[10]> ClassFour.method_y')

if __name__ == '__main__':
print('<[11]> ClassOne tests', 30 * '.')
one = ClassOne()
one.method_x()
print('<[12]> ClassThree tests', 30 * '.')
three = ClassThree()
three.method_y()
print('<[13]> ClassFour tests', 30 * '.')
four = ClassFour()
four.method_y()

print('<[14]> evaltime module end')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# evalsupport.py
print('<[100]> evalsupport module start')

def deco_alpha(cls):
print('<[200]> deco_alpha')

def inner_1(self):
print('<[300]> deco_alpha:inner_1')

cls.method_y = inner_1
return cls

class MetaAleph(type):
print('<[400]> MetaAleph body')

def __init__(cls, name, bases, dic):
print('<[500]> MetaAleph.__init__')

def inner_2(self):
print('<[600]> MetaAleph.__init__:inner_2')

cls.method_z = inner_2

print('<[700]> evalsupport module end')

在第一种情况下,输出的结果是:

1
2
3
4
5
6
7
8
9
10
<[100]> evalsupport module start
<[400]> MetaAleph body
<[700]> evalsupport module end
<[1]> evaltime module start
<[2]> ClassOne body
<[6]> ClassTwo body
<[7]> ClassThree body
<[200]> deco_alpha
<[9]> ClassFour body
<[14]> evaltime module end

可以看到,解释器会先处理模块的依赖,并且执行每个类以及嵌套类的定义体,而装饰器函数在定义的时候没有被执行,当需要装饰类的时候,类的定义体首先被执行(这很合理,有了类才能被装饰),然后执行了装饰器函数。

由于是通过 import 导入,所以 if __name__ == '__main__' 块不会被执行。

对于第二种情况,输出的结果是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<[100]> evalsupport module start
<[400]> MetaAleph body
<[700]> evalsupport module end
<[1]> evaltime module start
<[2]> ClassOne body
<[6]> ClassTwo body
<[7]> ClassThree body
<[200]> deco_alpha
<[9]> ClassFour body
<[11]> ClassOne tests ..............................
<[3]> ClassOne.__init__
<[5]> ClassOne.method_x
<[12]> ClassThree tests ..............................
<[300]> deco_alpha:inner_1
<[13]> ClassFour tests ..............................
<[10]> ClassFour.method_y
<[14]> evaltime module end
<[4]> ClassOne.__del__

前面几行和第一种情况是一致的,在 __main__ 块里,可以看到各个类的方法被执行了,而 ClassThree 的类方法被装饰器替换了,最后程序结束的时候,绑定在全局变量上的 ClassOne 实例被 GC 释放。