如題
英文官方文檔: https://docs.python.org/3.8/tutorial/classes.html
中文官方文檔: https://docs.python.org/zh-cn/3.8/tutorial/classes.html
目錄
1、類定義
2、類對象
3、執行個體對象
4、方法對象
5、類和執行個體變量
6、補充說明
類提供了一種組合資料和功能的方法。
建立一個新類意味着建立一個<code>新的對象類型</code>,進而允許建立一個該類型的新執行個體。
最簡單的類定義看起來像這樣:
類定義與函數定義 (def 語句) 一樣必須被執行才會起作用。
當進入類定義時,将建立一個新的命名空間,并将其用作局部作用域。是以,所有對局部變量的指派都是在這個新命名空間之内。 特别的,函數定義會綁定到這裡的新函數名稱。
類對象支援兩種操作:屬性引用和執行個體化。
(1)屬性引用
屬性引用的标準文法: <code>obj.name</code>。
有效的屬性名稱是,類對象被建立時,存在于類命名空間中的所有名稱。 是以,如果類定義是這樣的:
那麼 <code>MyClass.i</code> 和 <code>MyClass.f</code> 就是有效的屬性引用,将分别傳回一個整數和一個函數對象。
類屬性也可以被指派,是以可以通過指派來更改 <code>MyClass.i</code> 的值。
<code>__doc__</code>也是一個有效的屬性,将傳回所屬類的文檔字元串: "A simple example class"。
(2)執行個體化
類的執行個體化使用函數表示法。
可以把類對象視為是傳回該類的一個新執行個體的不帶參數的函數。 舉例來說(假設使用上述的類):
建立類的新執行個體并将此對象配置設定給局部變量 x。
執行個體化操作(“調用”類對象)會建立一個空對象,也可以使用<code>__init__()</code> 建立帶有特定初始狀态的自定義執行個體。例如:
此時,類的執行個體化操作會自動為新建立的類執行個體調用 <code>__init__()</code>。 是以在這個示例中,可以通過 <code>x = MyClass()</code> 語句獲得一個經初始化的新執行個體:
<code>__init__()</code> 方法還可以有額外參數以實作更高靈活性。在這種情況下,提供給類執行個體化運算符的參數将被傳遞給 <code>__init__()</code>。 例如,:
執行個體對象唯一操作是屬性引用。 有兩種有效的屬性名稱:資料屬性和方法。
(1)資料屬性
資料屬性不需要聲明,像局部變量一樣,它們将在第一次被指派時産生。
例如,如果 x 是上面建立的 MyClass 的執行個體,則以下代碼段将列印數值 16,且不保留任何追蹤資訊:
(2)方法
方法是“從屬于”對象的函數。 【注:方法是針對對象來說的,函數是針對類來說的】
(在 Python 中,方法這個術語并不是類執行個體所特有的:其他對象也可以有方法。例如,清單對象具有 append, insert, remove, sort 等方法。然而,在以下讨論中,我們使用方法一詞将專指類執行個體對象的方法,除非另外顯式地說明。)
執行個體對象的有效方法名稱依賴于其所屬的類。 【注:這裡說的是方法名稱】
根據定義,一個類中所有是函數對象的屬性都是定義了其執行個體的相應方法。
是以在我們的示例中,x.f 是有效的方法引用,因為 MyClass.f 是一個函數,而 x.i 不是方法,因為 MyClass.i 不是一個函數。 但是x.f 與 MyClass.f 并不是一回事,它是一個方法對象,不是函數對象。
通常,方法在綁定後立即被調用,在 MyClass 示例中,這将傳回字元串 'hello world'。
但是,立即調用一個方法并不是必須的: x.f 是一個方法對象,它可以被儲存起來以後再調用。 例如:
将繼續列印 hello world,直到結束。
雖然 <code>f()</code> 的函數定義指定了一個參數,但在上面調用 <code>x.f()</code> 時并沒有帶參數。 當不帶參數地調用一個需要參數的函數時 Python 肯定會引發異常,即使參數實際未被使用。
方法的特殊之處就在于執行個體對象會作為函數的第一個參數被傳入。 在我們的示例中,調用 x.f() 其實就相當于 MyClass.f(x)。 【注:也就是方法參數清單中的self】
總之,調用一個具有 n 個參數的方法就相當于調用再多一個參數的對應函數,這個參數值為方法所屬執行個體對象,位置在其他參數之前。
當一個執行個體的非資料屬性【注:即方法】被引用時,将搜尋執行個體所屬的類。
如果被引用的屬性名稱表示一個有效的類屬性中的函數對象,會通過打包(指向)查找到的執行個體對象和函數對象 到一個抽象對象的方式來建立方法對象:這個抽象對象就是方法對象。 【注:xf = x.f,x.f 是一個方法對象】
當附帶參數清單調用方法對象時,将基于執行個體對象和參數清單建構一個新的參數清單【注:self和參數清單】,并使用這個新參數清單調用相應的函數對象。
一般來說,執行個體變量用于每個執行個體的唯一資料,而類變量用于類的所有執行個體共享的屬性和方法:
【注:下例中<code>kind</code>是類變量,<code>name</code>是執行個體變量】
正如 名稱和對象 中已讨論過的,共享資料可能在涉及可變對象的時候,例如清單和字典,導緻令人驚訝的結果。
例如以下代碼中的 tricks 清單不應該被用作類變量,因為所有的 Dog 執行個體将隻共享一個單獨的清單:
【注:類變量是所有執行個體所共享的,以下代碼中的 tricks 清單不應該被用作類變量,執行個體調用 add_trick 時,就改變了 tricks 清單】
正确的類設計應該使用執行個體變量:
如果同樣的屬性名稱同時出現在執行個體和類中,則屬性查找會優先選擇執行個體:
方法的第一個參數常常被命名為 self。 這也不過就是一個約定: self 這一名稱在 Python 中絕對沒有特殊含義。但是要注意,不遵循此約定會使得你的代碼對其他 Python 程式員來說缺乏可讀性,而且也可以想像一個 類浏覽器 程式的編寫可能會依賴于這樣的約定。
任何一個作為類屬性的函數都為該類的執行個體定義了一個相應方法。 函數定義的文本并非必須包含于類定義之内:将一個函數對象指派給一個局部變量也是可以的。 例如:
現在 f, g 和 h 都是 C 類的引用函數對象的屬性,因而它們就都是 C 的執行個體的方法,其中 h 完全等同于 g。 但請注意,本示例的做法通常隻會令程式的閱讀者感到迷惑。
方法可以通過使用 self 參數的方法屬性調用其他方法:
方法可以通過與普通函數相同的方式引用全局名稱。與方法相關聯的全局作用域就是包含其定義的子產品。(類永遠不會被作為全局作用域。)
雖然我們很少會有充分的理由在方法中使用全局作用域,但全局作用域存在許多合法的使用場景:舉個例子,導入到全局作用域的函數和子產品可以被方法所使用,在其中定義的函數和類也一樣。
通常,包含該方法的類本身是在全局作用域中定義的,而在下一節中我們将會發現為何方法需要引用其所屬類的很好的理由。
每個值都是一個對象,是以具有 類(也稱為 類型),并存儲為 <code>object.__class__</code>。