Tornado-6.0.3源码分析之IO Event 事件的Configurable类

一、前述

要想理解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类的使用方法,可以直接实例化间接子类,也可能实例化其直接子类,从而得到默认想实例化的间接子类。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章