天天看點

《Python面向對象程式設計指南》——2.8 __new__()方法和不可變對象

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

<code>__new__</code>方法的一個用途是初始化不可變對象。__new__()方法中允許建立未初始化的對象。這允許我們在__init__()方法被調用之前先設定對象的屬性。

由于不可變類的__init__()方法很難重載,是以__new__方法提供了一種擴充這種類的方法。

下面是一個錯誤定義的類,我們定義了float的一個包含機關資訊的版本。

我們試圖(不合理地)初始化一個不可變對象。

下面是當我們試圖使用這個類時會發生的情況。

可以看到,對于内置的float類,我們不能簡單地重載__init__方法。對于其他的内置不可變類型,也有類似的問題。我們不能在不可變對象self上設定新的屬性值,因為這是不可變性的定義。我們隻能在對象建立的過程中設定屬性值,對象建立之後__new__()方法就會被調用。

__new__()方法天生就是一個靜态方法。即使沒有使用@staticmethod修飾符,它也是靜态的。它沒有使用self變量,因為它的工作是建立最終會被指派給self變量的對象。

這種情況下,我們會使用的方法簽名是__new__( cls, args, *kw)。cls變量是準備建立的類的執行個體。下一個部分關于元類型的例子,會比這裡展示的args的參數序列更加複雜。

__new__()方法的預設實作如下。

return super().__new__( cls )将調用基類的__new__()方法建立對象。這個工作最終委托給了object.__new__(),這個方法建立了一個簡單的空對象。除了cls以外,其他的參數和關鍵字最終都會傳遞給__init__()方法,這是python定義的标準行為。

除了有下面的兩個例外,這就是我們期望的行為。

當我們需要繼承一個不可變的類的時候,我們會在後面的部分詳細講解。

當我們需要建立一個元類型的時候,這是下一個部分的主題,因為它與建立不可變對象是完全不同的。

當建立一個内置的不可變類型的子類時,不能重載__init__()方法。取而代之的是,我們必須通過重載__new__()方法在對象建立的過程中擴充基類的行為。下例是擴充float類的正确方式。

上面的代碼在對象建立的過程中設定了一個屬性的值。

下面的代碼使用上面定義的類建立了一個帶機關的浮點數。

注意,像speed * 10這種表達式不會建立一個float_units對象。這個類的定義繼承了float中所有的運算符;float的所有算術特殊方法也都隻會建立float對象。建立float_units對象會在第7章“建立數值類型”中介紹。