一、前述
要想理解tornado的io event实现,主要是要学习熟悉python 标准库的asyncio库。对于这个库的API的,python在规划是分为两个层次的,如果是为了理解Tornado库,至少要先理解下,Asyncio库的Future相关知识和Event Loop相关知识。讲解说明Asyncio库,不在这篇文章的范围内。
二、结构说明
对于Tornado的IOLoop的整体实现,所涉及的模块主要是ioloop.py和platform文件夹下的asyncio.py。当然了,还有其它一些相干的公共模块。下面先来直观看下,各主要的类间的相互关系。
从这个图中可以直观看到,IOLoop类,是实现了Configurable类的接口,而其本身又是其它类的子类。对于更上层来说,访问的相关接口,统一从IOLoop类给出,并不直接访问AsyncIOMainLoop类或者AsyncIOLoop类。从IOLoop类的注释中可以看到,IOLoop类是asyncio库的包装,包装的接口定义是在IOLoop类中给出,而具体实现,其实是由其子类AsyncIOLoop相关类来完成。下面就以基本的继承关系来说明具体实现细节。
三、Configurable类
Configurable类的注释说明讲到,这个类接口就相当于一个工厂方法,可以用来构造生成其特定的子类。至于具体是实例化哪个子类,可以在程序运行的时候的,在需要的地方,通过Configurable.Configure方法动态指定;同时这里要注意的是,继承至这个类的子类,其真正初始化相关参数的地方,是在Configurable.initialize方法,而不是传统的__init__方法;当然了之所以选择了定义一个initialize方法,而不是选择__init__方法,当前是为了兼容AsyncHTTPClient的实现。
这个类最重要的理解其__new__方法实现;而对于这个类的使用方法,一般是定义一个类直接继承Configurable,暂且称这个子类为直接子类(如上述的IOloop类),直接子类进一步定义接口,然后再定义其它类继承该直接子类,暂且称这些类为间接子类(如上述的AsyncIOLoop等),间接子类实现直接子类新定义的功能接口,最后在使用方法上,可以直接实例化间接子类,也可以实例化直接子类,但是实例化直接子类时,一般会被转化为实例化某个间接子类。下面从源码来看如何实现:
首先纵望整个Configurable类,发现其直接子类必须要重载实现两个方法:
- configurable_base:返回相应的直接子类的类名
- configurable_default:返回默认使用的间接子类的类名。这个间接子类的使用时机是当Configurable类没有被调用过configure方法进行配置,也没有被初始化过时,这个configurable_default返回的默认类会被用来初始化。
# 这里定义两个类成员变量,用来记录,当需要实例化,是实例化哪个子类
__impl_class = None # 表示当前被配置上的间接子类的类名
__impl_kwargs = None # 实例化该子类时所需的参数,这是一个字典变量
先看一下configured_class方法实现,该方法的实际作用是返回当前被配置上的间接子类的类名
def configured_class(cls):
base = cls.configurable_base()
# 从当前这个直接子类的属性集 __dict__ 上检查,__impl_class这个私有的类变量是否有值
# 若有值,意味着当前这个直接子类有被实例化过,
# 或者是通过Configurable.configure方法配置上来的。
if base.__dict__.get("_Configurable__impl_class") is None:
# 若当前这个直接子类没有被配置上需要实例化的相应的间接子类,
# 则获取该直接子类默认配置的间接子类
base.__impl_class = cls.configurable_default()
if base.__impl_class is not None:
return base.__impl_class
else:
raise ValueError("configured class not found")
理解了以上的功能说明后,下面来重点看下实例化过程中,进行实例化拦截的实现 new 方法
# 这里重载定义__new__方法,拦截类的实例化过程
# 传统上对于类的实例化,调用顺序是先调用到__new__方法,再调用到__init__方法。
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
# 这里通过configurable_base方法,返回待实例化的类cls的直接子类的类名
base = cls.configurable_base()
init_kwargs = {} # type: Dict[str, Any]
if cls is base:
# 如果当前要实例化的类cls,就是直接继承Configurable的直接子类,
# 则通过configured_class方法返回具体需要实例化的间接子类的类名。
# 因此当cls是base时,真正实例化出来的不一定是base类的具体实例,而有可能是其子类实例。
impl = cls.configured_class()
if base.__impl_kwargs:
init_kwargs.update(base.__impl_kwargs)
else:
impl = cls
init_kwargs.update(kwargs)
# 当前待实例化的类impl的要求的直接子类不是当前cls类所配置记录的直接子类时,
# 直接递归初始化当前待实例化的类impl。
# 这个场景在下面用一个例子来说明。
if impl.configurable_base() is not base:
return impl(*args, **init_kwargs)
# 这里就是真正调用Object类去实例化一个类。
instance = super(Configurable, cls).__new__(impl)
# 创建实例后,就调用initialize方法,进一步完成相应的初始化操作
instance.initialize(*args, **init_kwargs)
return instance
下面举个例子说明,这个类的功能。为了说明问题,我在Configurable类中添加了一个打印。
class Configurable(object):
__impl_class = None
__impl_kwargs = None
def __new__(cls, *args, **kwargs):
base = cls.configurable_base()
init_kwargs = {}
print(f"base={base}")
print(f"cls={cls}")
if cls is base:
impl = cls.configured_class()
if base.__impl_kwargs:
init_kwargs.update(base.__impl_kwargs)
else:
impl = cls
print(f"impl={impl}")
init_kwargs.update(kwargs)
if impl.configurable_base() is not base:
print(f"impl.configurable_base={impl.configurable_base()}")
return impl(*args, **init_kwargs)
instance = super(Configurable, cls).__new__(impl)
instance.initialize(*args, **init_kwargs)
return instance
@classmethod
def configurable_base(cls):
raise NotImplementedError()
@classmethod
def configurable_default(cls):
raise NotImplementedError()
def _initialize(self) -> None:
pass
initialize = _initialize
@classmethod
def configured_class(cls):
base = cls.configurable_base()
if base.__dict__.get("_Configurable__impl_class") is None:
base.__impl_class = cls.configurable_default()
if base.__impl_class is not None:
return base.__impl_class
else:
raise ValueError("configured class not found")
@classmethod
def configure(cls, impl, **kwargs):
base = cls.configurable_base()
base.__impl_class = impl
base.__impl_kwargs = kwargs
class A(Configurable):
@classmethod
def configurable_base(cls):
return A
@classmethod
def configurable_default(cls):
return A1
class A1(A):
pass
class B(A):
@classmethod
def configurable_base(cls):
return B
@classmethod
def configurable_default(cls):
return B1
class B1(B):
pass
if __name__ == '__main__':
a = A()
print("==================================")
A.configure(B)
b = A()
这个程序的输出如下所示:
base=<class '__main__.A'>
cls=<class '__main__.A'>
impl=<class '__main__.A1'>
==================================
base=<class '__main__.A'>
cls=<class '__main__.A'>
impl=<class '__main__.B'>
impl.configurable_base=<class '__main__.B'>
base=<class '__main__.B'>
cls=<class '__main__.B'>
impl=<class '__main__.B1'>
先来解释下这个例子,在这个例子中Configurable的直接子类是A类,且A重载configurable_base方法,返回基类类名A,重载configurable_default方法,返回默认的实现是间接子类A1。因此,当执行 a = A()时,想被直接实例的类cls是A,即cls=<class ‘main.A’>,而此时A.configurable_base也是A,即base=<class ‘main.A’>,所以在__new__方法中,if cls is base条件成立,就调用impl = cls.configured_class()去查找真正需要被实例化的类,因为此时base.__impl_class还是None值,所以最终是调用了base.__impl_class = cls.configurable_default(),这里就是调用了A.configurable_default()方法,所以这里返回的真正要实例化的类,是类A1,即impl=A1。
然后再解释下,另一组输出。
首先B也是继承自A类,即属于Configurable类的间接子类,但是B类也重载了configurable_base和configurable_default方法,设定默认返回的实现类是B1。
接下来通过配置接口,A.configure(B),修改,使得A.__impl_class=B。
当调用b = A()时,一样的,从语法上看,想直接实例化的是类A,因此cls=A,而A.configurable_base也是A,即base=A。这时if cls is base条件成立,就调用impl = cls.configured_class()去查找真正需要被实例化的类,注意此时base.__impl_class已经是刚才通过A.configure(B)修改过的了,因此得到真正想实例化的是B,而B.configurable_base是等于B的,进而if impl.configurable_base() is not base条件成立,就执行return impl(*args, **init_kwargs)语句,进行递归实例化。走到这里,就相当于执行了b = B(),然后就以实例化B类执行,从而有了后面的最后的三句输出
四、总结
在这篇里面,要理解继承Configurable类的使用方法,可以直接实例化间接子类,也可能实例化其直接子类,从而得到默认想实例化的间接子类。