天天看點

《Python面向對象程式設計指南》——1.11 多政策的__init__()方法

本節書摘來自異步社群《python面向對象程式設計指南》一書中的第1章,第1.11節,作者[美]steven f. lott, 張心韬 蘭亮 譯,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

有些對象的建立來自多個來源。例如,我們也許需要克隆一個對象作為備忘錄模式的一部分,或者當機一個對象以使它可以用來作為字典的鍵或放入哈希集合;這也是set和fronezenset類的實作方式。

有很多全局的設計模式使用了多種方式來建立對象。其中一個為多政策初始化,__init__()函數的實作邏輯較為複雜,也會用到類層次結構中不同的(靜态)構造函數。

它們是非常不同的實作方式,在接口的定義上就有根本差別。

《Python面向對象程式設計指南》——1.11 多政策的__init__()方法

之前的例子可以被高效的克隆是因為它們非常簡單,在下一章中會進行展開描述。然而,為了更詳細地說明更多關于對象克隆的基本技巧,我們會讨論一下如何把可變的hand對象當機成為不可變的hand對象。

以下代碼示範了兩種建立hand對象的例子。

第1種方式,hand3執行個體從已有的hand3對象建立。第2種方式,hand3對象的建立基于card執行個體。

一個fronzenset對象的建立可以基于已有的執行個體,或基于已存在集合對象。下一章會具體介紹建立不可變對象。基于已有的hand,建立一個hand對象,可用于建立一個hand對象的備忘錄模式,例如下面這段實作。

我們使用mem ento變量來儲存hand對象。可以用來比較目前對象和之前被處理的對象,我們也可以當機它用于集合或映射。

為了将多政策應用于初始化,通常要被迫放棄顯式命名的參數。這樣的設計雖然獲得了靈活性,卻使得參數名不夠透明,意圖不夠明顯,需要針對不同的使用場景分别提供文檔進行解釋說明。

也可以擴充初始化的實作來分離hand對象。要分離的hand對象隻需修改構造函數。以下代碼段示範了如何分離一個hand對象。

這個設計需要傳入更多的紙牌對象來建立合适的、分離的hand對象。當我們從一個hand4對象中分離出另一個hand4對象時,使用split參數作為索引從原hand4對象中讀取card對象。以下代碼示範了我們怎樣分離出一個hand對象。

我們初始化了一個hand4類的執行個體然後再分離出其他的hand4執行個體,命名為s1和s2,然後将card對象傳入每個hand對象。在21點的規則中,隻有當手中兩張牌大小相等的時候才可允許分牌。可以看到__init__()函數的邏輯已經非常複雜,優勢在于,它可以基于已有集合同時建立多個像fronzenset這樣的對象。然而也将需要更多的注釋和文檔來說明這些行為。

當我們有多種方式來建立一個對象時,有時使用靜态函數好過使用複雜的__init__()函數。

也可以考慮使用類函數作為初始化的另一種選擇,然而将依賴的對象作為參數傳入函數會更好。當當機或分離一個hand對象時,我們或許希望建立兩個新的靜态函數來完成任務。使用靜态函數作為代理構造函數在文法上略有差别,但是在代碼的組織上卻有明顯的優勢。

以下是hand類的實作,使用了靜态函數來完成初始化,從已有的hand執行個體建立兩個新執行個體。

使用一個函數完成了當機和備忘錄模式,用另一個函數将hand5對象分離為兩個子執行個體。

這樣既可以增強可讀性,也不必使用參數名稱來解釋接口意圖。

以下代碼段示範了我們如何把hand5對象進行分離:

我們建立了一個hand5類的h執行個體,把它分為另外兩個hand執行個體,名為s1和s2,然後分别為它們指派。而使用__init__()函數實作同樣的功能時,split()靜态函數的實作版本簡化了很多。然而它并沒有遵守一個原則:使用已有的set對象來建立fronzenset對象。