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類的使用方法,可以直接實例化間接子類,也可能實例化其直接子類,從而得到默認想實例化的間接子類。

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