前文讲述了变量在命名空间中的作用域规则,Python中每个代码段都有自己独立的命名空间,而函数又是以怎样的一种方式在模块、被嵌套函数的命名空间中存在呢?
这是一段含有嵌套函数的源代码,main函数调用了func函数,func函数有调用了inner_func函数,也即是有两处出现了函数的嵌套。下面是通过dis模块提取出来的字节码信息。
我们可以看到func在main栈帧运行环境中以全局变量存在,LOAD_GLOBAL。实际上保存在了frame对象中的f_globals当中,而f_globals是一个PyDictObject类型的字典对象,而字典对象又是无序存在的,只要在运行之前能正确编译出字节码对象即可。
看上去,函数和变量的作用域规则似乎有点像,实际上函数在栈帧运行环境中被看作一个全局变量来处理的。那类又是怎么样的呢,和函数有什么不同呢?
在Python内部,所有的动西都是对象,即使是用户通过class关键字定义的类也是一样,叫做类对象,这是在Python内部实现的层面上。通过class关键字定义的类又可以实例化出具体的对象,我们暂且叫做实例对象,这是从Python外部使用层面上来看的。想要了解类在命名空间中的作用域规则,首先要清楚类对象是如何产生的,以什么样的形式存在,会不会和函数一样也是作为全局变量在模块的命名空间存在呢?
Python的类对象都是通过元类来产生的,默认元类为,用户也可以通过metaclass关键字自定义元类。
classSuperTest():
def__init__(self):
self.name =''
def__repr__(self):
return'Super_ins'
classTest(SuperTest):
frame = sys._getframe()
printframe.f_globals
def__init__(self):
self.name ='test'
defmain():
t= Test()
main()
图示例源码中定义了两个有继承关系的类,在Test的栈中f_globals包含SuperTest 对象,'SuperTest': 。
classTest(SuperTest):
frame = sys._getframe()
printframe.f_globals
def__init__(self):
self.name ='test'
classSuperTest():
def__init__(self):
self.name =''
def__repr__(self):
return'Super_ins'
而如果把SuperTest定义在Test的后面,NameError的错误。
NameError: name 'SuperTest' is not defined
先来看下类对象生成过程:
首先会检查自定义类是否有基类,如果没有就默认为object。如果有就创建基类列表,为Tuple类型,实际上保存基类变量名列表。然后非别对基类进行初始化,初始化的过程就是绑定类对象元信息的过程(实际上就是用户在class中定义的数据填充到类对象tp_dict中去)。在基类初始化过程通过LOAD_NAME获取基类的时候,没有找到变量的约束关系,因为实际的约束关系绑定在后面的定义中实现的。所以就会报出NameError的错误。也就是说在有继承关系的类定义中是有先后顺序的。而函数是被当作全局变量保存在f_globals中与位置是无关的。
领取专属 10元无门槛券
私享最新 技术干货