類引入了一些新語法:三種新的對象類型和一些新的語義。
### 9.3.1\. 類定義語法
類定義最簡單的形式如下:
~~~
class ClassName:
.
.
.
~~~
類的定義就像函數定義(?def?語句),要先執行才能生效。(你當然可以把它放進?if?語句的某一分支,或者一個函數的內部。)
習慣上,類定義語句的內容通常是函數定義,不過其它語句也可以,有時會很有用,后面我們再回過頭來討論。類中的函數定義通常包括了一個特殊形式的參數列表,用于方法調用約定——同樣我們在后面討論這些。
進入類定義部分后,會創建出一個新的命名空間,作為局部作用域。因此,所有的賦值成為這個新命名空間的局部變量。特別是函數定義在此綁定了新的命名。
類定義完成時(正常退出),就創建了一個?_類對象_。基本上它是對類定義創建的命名空間進行了一個包裝;我們在下一節進一步學習類對象的知識。原始的局部作用域(類定義引入之前生效的那個)得到恢復,類對象在這里綁定到類定義頭部的類名(例子中是?ClassName?)。
### 9.3.2\. 類對象
類對象支持兩種操作:屬性引用和實例化。
_屬性引用_?使用和 Python 中所有的屬性引用一樣的標準語法:obj.name。類對象創建后,類命名空間中所有的命名都是有效屬性名。所以如果類定義是這樣:
~~~
class MyClass:
"""A simple example class"""
i = 12345
def f(self):
return 'hello world'
~~~
那么?MyClass.i?和?MyClass.f?是有效的屬性引用,分別返回一個整數和一個方法對象。也可以對類屬性賦值,你可以通過給?MyClass.i?賦值來修改它。?__doc__?也是一個有效的屬性,返回類的文檔字符串:"A?simple?example?class"。
類的?_實例化_?使用函數符號。只要將類對象看作是一個返回新的類實例的無參數函數即可。例如(假設沿用前面的類):
~~~
x = MyClass()
~~~
以上創建了一個新的類?_實例_?并將該對象賦給局部變量?x。
這個實例化操作(“調用”一個類對象)來創建一個空的對象。很多類都傾向于將對象創建為有初始狀態的。因此類可能會定義一個名為?__init__()?的特殊方法,像下面這樣:
~~~
def __init__(self):
self.data = []
~~~
類定義了?__init__()?方法的話,類的實例化操作會自動為新創建的類實例調用?__init__()?方法。所以在下例中,可以這樣創建一個新的實例:
~~~
x = MyClass()
~~~
當然,出于彈性的需要,__init__()?方法可以有參數。事實上,參數通過?__init__()?傳遞到類的實例化操作上。例如,
~~~
>>> class Complex:
... def __init__(self, realpart, imagpart):
... self.r = realpart
... self.i = imagpart
...
>>> x = Complex(3.0, -4.5)
>>> x.r, x.i
(3.0, -4.5)
~~~
### 9.3.3\. 實例對象
現在我們可以用實例對象作什么?實例對象唯一可用的操作就是屬性引用。有兩種有效的屬性名。
_數據屬性_?相當于 Smalltalk 中的“實例變量”或 C++ 中的“數據成員”。和局部變量一樣,數據屬性不需要聲明,第一次使用時它們就會生成。例如,如果?x?是前面創建的?MyClass?實例,下面這段代碼會打印出 16 而在堆棧中留下多余的東西:
~~~
x.counter = 1
while x.counter 10:
x.counter = x.counter * 2
print(x.counter)
del x.counter
~~~
另一種為實例對象所接受的引用屬性是?_方法_。方法是“屬于”一個對象的函數。(在 Python 中,方法不止是類實例所獨有:其它類型的對象也可有方法。例如,鏈表對象有 append,insert,remove,sort 等等方法。然而,在后面的介紹中,除非特別說明,我們提到的方法特指類方法)
實例對象的有效名稱依賴于它的類。按照定義,類中所有(用戶定義)的函數對象對應它的實例中的方法。所以在我們的例子中,x.f?是一個有效的方法引用,因為?MyClass.f?是一個函數。但x.i?不是,因為?MyClass.i?不是函數。不過?x.f?和?MyClass.f?不同,它是一個?_方法對象_?,不是一個函數對象。
### 9.3.4\. 方法對象
通常,方法通過右綁定方式調用:
`x.f()`
在?MyClass?示例中,這會返回字符串?'hello?world'。然而,也不是一定要直接調用方法。?x.f?是一個方法對象,它可以存儲起來以后調用。例如:
~~~
xf = x.f
while True:
print(xf())
~~~
會不斷的打印?hello?world。
調用方法時發生了什么?你可能注意到調用?x.f()?時沒有引用前面標出的變量,盡管在?f()?的函數定義中指明了一個參數。這個參數怎么了?事實上如果函數調用中缺少參數,Python 會拋出異常--甚至這個參數實際上沒什么用……
實際上,你可能已經猜到了答案:方法的特別之處在于實例對象作為函數的第一個參數傳給了函數。在我們的例子中,調用?x.f()?相當于?MyClass.f(x)?。通常,以?_n_?個參數的列表去調用一個方法就相當于將方法的對象插入到參數列表的最前面后,以這個列表去調用相應的函數。
如果你還是不理解方法的工作原理,了解一下它的實現也許有幫助。引用非數據屬性的實例屬性時,會搜索它的類。如果這個命名確認為一個有效的函數對象類屬性,就會將實例對象和函數對象封裝進一個抽象對象:這就是方法對象。以一個參數列表調用方法對象時,它被重新拆封,用實例對象和原始的參數列表構造一個新的參數列表,然后函數對象調用這個新的參數列表。
- Python 入門指南
- 1. 開胃菜
- 2. 使用 Python 解釋器
- 2.1. 調用 Python 解釋器
- 2.2. 解釋器及其環境
- 3. Python 簡介
- 3.1. 將 Python 當做計算器
- 3.2. 編程的第一步
- 4. 深入 Python 流程控制
- 4.1. if 語句
- 4.2. for 語句
- 4.3. range() 函數
- 4.4. break 和 continue 語句, 以及循環中的 else 子句
- 4.5. pass 語句
- 4.6. 定義函數
- 4.7. 深入 Python 函數定義
- 4.8. 插曲:編碼風格
- 5. 數據結構
- 5.1. 關于列表更多的內容
- 5.2. del 語句
- 5.3. 元組和序列
- 5.4. 集合
- 5.5. 字典
- 5.6. 循環技巧
- 5.7. 深入條件控制
- 5.8. 比較序列和其它類型
- 6. 模塊
- 6.1. 深入模塊
- 6.2. 標準模塊
- 6.3. dir() 函數
- 6.4. 包
- 7. 輸入和輸出
- 7.1. 格式化輸出
- 7.2. 文件讀寫
- 8. 錯誤和異常
- 8.1. 語法錯誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預定義清理行為
- 9. 類
- 9.1. 術語相關
- 9.2. Python 作用域和命名空間
- 9.3. 初識類
- 9.4. 一些說明
- 9.5. 繼承
- 9.6. 私有變量
- 9.7. 補充
- 9.8. 異常也是類
- 9.9. 迭代器
- 9.10. 生成器
- 9.11. 生成器表達式
- 10. Python 標準庫概覽
- 10.1. 操作系統接口
- 10.2. 文件通配符
- 10.3. 命令行參數
- 10.4. 錯誤輸出重定向和程序終止
- 10.5. 字符串正則匹配
- 10.6. 數學
- 10.7. 互聯網訪問
- 10.8. 日期和時間
- 10.9. 數據壓縮
- 10.10. 性能度量
- 10.11. 質量控制
- 10.12. “瑞士軍刀”
- 11. 標準庫瀏覽 – Part II
- 11.1. 輸出格式
- 11.2. 模板
- 11.3. 使用二進制數據記錄布局
- 11.4. 多線程
- 11.5. 日志
- 11.6. 弱引用
- 11.7. 列表工具
- 11.8. 十進制浮點數算法
- 12. 接下來?
- 13. 交互式輸入行編輯歷史回溯
- 13.1. 行編輯
- 13.2. 歷史回溯
- 13.3. 快捷鍵綁定
- 13.4. 其它交互式解釋器
- 14. 浮點數算法:爭議和限制
- 14.1. 表達錯誤
- 15. 附錄
- 15.1. 交互模式