1. 导入时与运行时
我们得知道Python解释器什么时候计算各个代码块.Python程序员会区分"导入时"和"运行时",不过这两个术语没有严格的定义,而且二者之间存在着灰色地带. 毕竟导入动作是可以在运行时执行的.
在导入时,解释器会从上到下一次性解析完.py
模块的源码,然后生成用于执行的字节码,需要注意的是导入模块是从来是最小化导入,如果父模块的__init__.py
中没有导入其下的子模块,那么直接导入父模块的话子模块是不会被导入的(也不会生成子模块的.pyc
).如果导入时句法有错误,就在此时报告.
导入也有一个缓存机制,其判别方式是依靠比对文件的更新时间戳,如果本地的__pycache__
文件夹中有最新的.pyc
文件,解释器会跳过上述导入步骤,因为已经有运行所需的字节码了.
编译肯定是导入时的活动,不过那个时期还会做些其他事,因为Python中的语句几乎都是可执行的,也就是说语句可能会运行用户代码,修改用户程序的状态.尤其是import
语句,它不只是声明,在进程中首次导入模块时,还会运行所导入模块中的全部顶层代码——以后导入相同的模块则使用缓存,只做名称绑定.那些顶层代码可以做任何事,包括通常在"运行时"做的事,例如连接数据库. 因此,“导入时”与“运行时”之间的界线是模糊的: import
语句可以触发任何"运行时"行为.
模块导入主要做的是讲函数和类的定义体加入模块的全局命名空间.
函数 导入模块时,解释器会执行顶层的
def
语句,可是这么做有什么作用呢?解释器会编译函数的定义体(首次导入模块时),把函数对象绑定到对应的全局名称上,但是显然解释器不会执行函数的定义体通常这意味着解释器在导入时定义顶层函数,但是仅当在运行时调用函数时才会执行函数的定义体.类 在导入时,解释器会执行每个类的定义体,甚至会执行嵌套类的定义体.执行类定义体的结果是,定义了类的属性和方法,并构建了类对象.从这个意义上理解,类的定义体属于“顶层代码”,因为它在导入时运行.
上述说明模糊又抽象,下面通过练习理解各个时期所做的事情。
%%writefile 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')
Overwriting evalsupport.py
%%writefile 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')
Overwriting evaltime.py
场景1: 导入模块
- 解释器会执行所导入模块及其依赖(evalsupport)中的每个类定义体。
- 解释器先计算类的定义体,然后调用依附在类上的装饰器函数,这是合理的行为,因为必须先构建类对象,装饰器才有类对象可处理。
- 在这个场景中,只运行了一个用户定义的函数或方法——
deco_alpha
装饰器。
import evaltime
<[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
场景2:执行evaltime.py
场景2 主要想说明的是,类装饰器可能对子类没有影响.在示例中, 我们把ClassFour
定义为ClassThree
的子类.ClassThree
类上依附的@deco_alpha
装饰器把method_y
方法替换掉了,但是这对ClassFour
类根本没有影响. 当然,如果ClassFour.method_y
方法使用super(...)
调用ClassThree.method_y
方法,我们便会看到装饰器起作用,执行inner_1
函数.
!python evaltime.py
<[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__
还没有评论,来说两句吧...