### 3. 數據模型
### 3.1 對象、值和類型
*對象*是Python對數據的抽象。Python程序中所有數據都由對象或對象之間的關系表示。(合理且與馮.諾依曼的“存儲程序計算機”模型一致,代碼也由對象表示。)
每個對象都有一個ID,一個類型和一個值。對象一旦建立,它的*ID*永遠不會改變;你可以認為它是該對象在內存中的地址。‘[is](#)’操作符比較兩個對象的ID;[id()](# "id")函數返回一個表示對象ID 的整數(當前實現為對象的地址)。對象的*類型*也是不可變的。[[1]](#)對象的類型決定了對象支持的操作(例如,“它有長度嗎?”)同時也定義了該種類型對象的可能的值。[type()](# "type")函數返回對象的類型(它本身也是一個對象)。某些對象的值可以改變。值可以改變的對象稱為*可變的*;一旦建立,值就不可以改變的對象稱為*不可變的*。(包含可變對象引用的不可變容器對象在可變對象改變時是可以改變的;但容器仍被看作是可變的, 因為它所包含的對象集合是不能變的。所以不可變對象與值不可變不是完全一樣的,它更加微妙。)一個對象的可變性由它的類型決定;例如,數值、字符串和元組是不可變的,而字典和列表是可變的。
對象不可以顯式地銷毀;但是當它們不可用時可能被當作垃圾回收。具體的實現可以推遲垃圾回收或完全忽略它 —?這是垃圾回收如何實現的質量問題,只要依然能訪問的對象不被回收。
**CPython 實現細節:**CPython 當前使用引用計數機制與(可選的)循環連接垃圾延遲檢測機制,一旦對象變得不可訪問,它將收集其中大部分,但是不保證收集包含循環引用的垃圾。參考[gc](# "gc: Interface to the cycle-detecting garbage collector.") 模塊的文檔可以獲得控制循環垃圾回收的信息。其它實現的行為與之不同并且CPython 的實現將來也可能會變化。不要依賴對象不可訪問后會立即終結(例如:永遠關閉文件)。
注意使用具體實現的跟蹤和調試工具可能會保持正常情況下可以回收的對象一直存活。還要注意使用‘[try](#)...[except](#)’語句捕獲異常也可能保持對象一直存活。
有些對象包含“外部”資源的引用,例如打開的文件或窗口。可以理解在對象被當作垃圾回收時這些資源也被釋放,但因為垃圾回收不保證一定發生,這樣的對象也提供顯式的方法釋放外部資源,通常是close()方法。強烈建議程序顯式關閉這些對象。‘[try](#)...[finally](#)’語句提供了一個便利的方式來做這件事。
有些對象包含其它對象的引用;它們叫做*容器*。容器的例子有元組,列表和字典。引用是容器的值的一部分。大多數情況下,當我們談到一個容器的值時,我們是指值,而不是所包含的對象的ID;然而,當我們談論容器對象的可變性的時候,就只是指被直接包含的對象的ID。因此,如果一個不可變對象(如元組)包含了一個可變對象的引用,那么當這個可變對象的值改變時它的值也發生改變。
對象的類型幾乎影響對象的所有行為。在某種意義上甚至重要到影響對象的識別:對于不可變對象,計算新值的運算符可能實際上返回的是一個已存在的具有相同類型和值的對象的引用,而對于可變對象,這是不允許的。例如,在a=1;b=1之后,a 和b可能是或者可能不是引用同一個值為1的對象,這依賴于實現,但c=[];d=[]之后,c 和d可以保證是引用兩個不同的、唯一的、新創建的空列表。(注意c=d=[]是把相同的對象賦給c 和d。)
### 3.2 標準類型的層次結構
以下是一份Python 內建類型的清單。擴展模塊(無論是用C、Java還是用其它語言編寫,依賴于具體實現)可以定義額外的類型。未來版本的Python 可能在這個類型層次機構中增加其它類型(例如:有理數、高效存儲的整數數組,等等)。
下面有些類型的描述包含一個列出“特殊屬性”的段落。這些屬性提供訪問具體的實現而不是作為一般的目的使用。它們的定義在未來可能會改變。
None
這種類型只有一個值。。只有一個對象具有這個值。這個對象通過內建名字None訪問。它在許多情況下用來表示沒有值,例如,沒有顯式返回任何內容的函數會返回它。它的真值為假。
NotImplemented
這種類型只有一個值。只有一個對象具有這個值。這個對象通過內建名字NotImplemented訪問。如果數值方法和復雜的比較方法沒有為提供的操作數實現某種運算,它們可能返回這個值。(解釋器會嘗試反射的操作,或者其它退化的操作,依賴于具體的運算符。)它的真值為真。
Ellipsis
這種類型只有一個值。只有一個對象具有這個值。這個對象通過內建名字Ellipsis訪問。它用于指示切片中出現的...語法。它的真值為真。
[numbers.Number](# "numbers.Number")
它們由數值字面量生成或者由算術運算符和內建的算術函數作為結果返回。數值對象是不可變的;一旦創建,它們的值永遠不會改變。Python 的數值和數學上的數字關系當然是非常密切的,但受到計算機數值表達能力的限制。
Python 區分整數、浮點數和復數:
[numbers.Integral](# "numbers.Integral")
它們表示數學上的整數集中的元素(包括正數和負數)。
有三種類型的整數:
普通整數
它們表示在-2147483648 至2147483647 范圍之間的數。(這個范圍可能會在本地機器字較大的機器更大些,但不會更小。)如果某個運算的結果超出這個范圍,結果會以長整數正常返回(在某些情況下,會拋出異常[OverflowError](# "exceptions.OverflowError"))。對于以移位和掩碼為目的的運算,整數采用32位或更多位的二進制補碼形式,并且不會對用戶隱藏任何位。(就是說,所有4294967296個不同的比特組合對應于不同的值)。
長整數
長整數的表示的數值范圍沒有限制,只受限于可用的(虛擬內存)內存。對于以移位和掩碼為目的的運算,長整數采用二進制的形式,負數用二進制補碼形式表示,給人的錯覺是一個符號位向左無限擴展的字符串。
布爾值
布爾值表示假和真的真值。表示False 和True 的兩個對象是僅有的布爾對象。布爾類型是普通整數的子類型,布爾值的行為在幾乎所有環境下分別類似0和1,例外的情況是轉換成字符串的時候分別返回字符串"False" 或"True"。
整數表示法的規則意在讓負數的移位和掩碼運算具有最有意義的解釋,并且在普通整數和長整數之間轉換時具有最少的意外。任何運算,只要它產生的結果在整數域之中,那么在長整數域或混合運算時將產生相同結果。域之間的轉換對程序員是透明的。
[numbers.Real](# "numbers.Real") ([float](# "float"))
這種類型表示機器級別的雙精度浮點數。你受底層的機器體系結構(和C或者Java的實現)控制接受的范圍和溢出處理。Python不支持單精度浮點數;使用它的原因通常是節省處理器和內存的使用,但是相比Python中對象使用的開銷是微不足道的,因此沒有必要支持兩種浮點數使語言變的復雜。
[numbers.Complex](# "numbers.Complex")
這種類型以一對機器級別的雙精度浮點數表示復數。單精度浮點數同樣可以用于復數類型。復數z的實部和虛部可以通過只讀屬性z.real和z.imag獲得。
序列
這種類型表示有限的順序集合,用非負數索引。內建的函數[len()](# "len") 返回序列的元素個數。當序列的長度為*n*時,索引集合包含數字0, 1, ..., *n*-1。序列*a*的元素*i* 的選擇使用a[i] 。
序列也支持切片:a[i:j]選擇索引*k*滿足*i*<=*k*<*j*的所有元素。作為表達式使用的時候,切片是一個相同類型的序列。這隱含著索引會從零開始重新計數。
某些序列還支持帶有第三個“步長”參數的“擴展切片”:a[i:j:k] 選擇*a* 中所有索引為*x*的 的元素,x=i+n*k, *n*>=0 且*i*<=*x*<*j*。
序列依據它們的可變性分為:
不可變序列不可變序列類型的對象一旦創建便不可改變。(如果這個對象包含其它對象的引用,這些引用的對象可以是可變的并且可以改變;然而不可變對象直接引用的對象集合不可改變。)
以下類型是不可變序列:
字符串
字符串的元素是字符。沒有單獨的字符類型;字符用一個元素的字符串表示。字符表示(至少)8 比特的字節。內建函數[chr()](# "chr") 和[ord()](# "ord")在字符和表示字節數值的非負整數之間轉換。值在0-127 之間的字節通常表示相應的ASCII 值,但是對值的解釋由程序決定。字符串數據類型也用于表示字節的數組,例如,保存從文件中讀取的數據。
(在原生字符集不是ASCII 的系統上,字符串在內部可以使用EBCDIC 表示,只要函數[chr()](# "chr") 和[ord()](# "ord") 實現ASCII 和EBCDIC 之間的映射并且字符串的比較保留ASCII 順序。或者可能有人能夠提出一個更好的規則?)
Unicode
Unicode 對象的元素是Unicode 編碼單元。一個Unicode 編碼單元由一個元素的Unicode 對象表示并且可以保持16位或者32位的值表示一個Unicode 序數。(序數的最大值在sys.maxunicode中給出,并依賴Python 在編譯的時候是如何配置的)Unicode 對象中可以表示代理對,并被當作兩個單獨的元素。內建的函數[unichr()](# "unichr") 和[ord()](# "ord")在編碼單元和表示定義在Unicode 標準3.0中Unicode 序數的非負整數之間轉換。和其它編碼之間相互轉換可以通過Unicode 方法encode() 和內建的函數[unicode()](# "unicode")。
元組
元組的元素可以是Python 的任何對象。兩個或多個元素的元組由逗號分隔的一連串表達式形成。一個元素的元組(單元素集)可以在一個表達式的后面附加一個逗號形成(一個表達式自身不會形成一個元組,因為圓括號必須可以用來分組表達式)。一個空的元組可以由一個空的圓括號對形成。
可變序列
可變序列在生成之后可以修改。下標和切片表示法可以用于賦值和[del](#) (delete)語句的對象。
當前有兩種內建的可變序列類型:
列表
列表的對象可以是Python 任何對象。列表由在方括號中放置一個逗號分隔的一連串表達式形成。(注意生成長度為0或1的列表沒有特殊的情形。)
字節數組
一個字節數組對象是一個可變的數組。它們由內建的[bytearray()](# "bytearray") 構造函數創建。除了可變性(因此不可哈希),另一方面字節數組提供和不可變字節對象同樣的接口和功能。
擴展模塊[array](# "array: Space efficient arrays of uniformly typed numeric values.")提供另外一個可變序列類型的例子。
集合類型
這種類型表示無序的、有限的集合,集合中的元素是唯一的、不可變的對象。正因如此,它們不可以被任何下標索引。然而,它們可以迭代,內建函數[len()](# "len")返回集合中元素的個數。集合常見的用途有快速成員關系檢測、從序列中刪除重復元素和計算數學運算例如交集、并集、差集和對稱差集。
集合的元素與字典的鍵一樣,都適用不可變規則。注意,數值類型遵循正常的數值比較規則:如果兩個數字相等(例如,1 和1.0),其中只有一個可以包含在集合中。
當前有兩種內建的集合類型:
集合
這種類型表示可變的集合。它們由內建函數[set()](# "set") 構造函數創建并可以在此之后通過幾種方法修改,例如[add()](# "set.add")。
固定集合
這種類型表示不可變集合。它們由內建函數[frozenset()](# "frozenset")構造器創建。因為固定集合不可變且[*可以哈希*](#),它可以作為另外一個集合的元素或者字典的鍵。
映射
這種類型表示由任意索引集合作索引的有限對象集合。下標表示法a[k] 從映射a 中選擇由k 索引的元素;它可以用在表達式中并作為賦值或[del](#)語句的目標。內建函數[len()](# "len")返回映射中元素的個數。
當前只有一個內建映射類型:
字典
這種類型表示幾乎可以由任何值索引的有限對象集合。不可以作為鍵的唯一類型是包含列表或者字典或者其它可變類型的值,這些類型通過值而不是對象ID比較,原因是字典的高效實現要求鍵的哈希值保持常量。注意,數值類型遵循正常的數值比較規則:如果兩個數字相等(例如,1 和1.0),那么它們可以互換地使用來索引同一個字典入口。
字典是可變的;它們可以通過{...} 表示法創建(參考[*Dictionary的顯示*](#)一節)。
擴展模塊[dbm](# "dbm: The standard "database" interface, based on ndbm. (Unix)"),[gdbm](# "gdbm: GNU’s reinterpretation of dbm. (Unix)") 和[bsddb](# "bsddb: Interface to Berkeley DB database library")提供另外的映射類型的例子。
可調用類型
這是一種可以使用函數調用操作(參考[*Calls*](#)一節)的類型:
用戶定義的函數
用戶定義的函數對象由函數定義創建(參見[*函數定義*](#)一節)。它調用時參數列表的元素個數應該和函數的形式參數列表相同。
特殊屬性:
| 屬性 | 含義 | ? |
|-----|-----|-----|
| __doc__func_doc | 函數的文檔字符串,如果沒有就為None。 | 可寫 |
| __name__func_name | 函數的名字。 | 可寫 |
| __module__ | 函數定義所在的模塊名,如果沒有就為None。 | 可寫 |
| __defaults__func_defaults | 為具有默認值的參數保存默認參數值的元組,如果沒有參數具有默認值則為None。 | 可寫 |
| __code__func_code | 表示編譯后的函數體的代碼對象。 | 可寫 |
| __globals__func_globals | 保存函數全局變量的字典的引用 — 函數定義所在模塊的全局命名空間。 | 只讀 |
| __dict__func_dict | 支持任意函數屬性的命名空間。 | 可寫 |
| __closure__func_closure | None 或者包含函數自由變量綁定的元組。 | 只讀 |
大部分標有“可寫”的屬性會檢查所賦值的類型。
版本2.4中的變化:func_name 成為如今可寫的屬性。
版本2.6中的變化:引入雙下劃線屬性__closure__, __code__, __defaults__, 和__globals__作為對應的func_*屬性的別名以向前兼容Python 3。
函數對象同樣支持獲取和設置任意屬性,這可以用來附加元數據到函數中。常規屬性可以用點號表示法獲取和設置。*注意當前的實現只在用戶定義的函數上支持函數屬性。未來可能支持內建函數上的函數屬性。*
函數定義的額外信息可以從它的代碼對象中獲取;參見下面內部類型的描述。
用戶定義的方法
用戶定義的方法將類、類的實例(或者None)和任何可調用對象(通常是一個用戶定義的函數)結合起來。
特殊的只讀屬性:im_self指類實例對象,im_func指函數對象;im_class對于綁定的方法指im_self的類,對于未綁定的方法指方法所在的類;__doc__指方法的文檔(與im_func.__doc__相同);__name__指方法的名字(與im_func.__name__一樣);__module__指方法定義所在模塊的名字,如果沒有則為None。
版本2.2中的變化:im_self過去指的是定義方法的類。
版本2.6中的變化:為了Python 3向前的兼容性,im_func也可以使用__func__訪問,im_self可以使用__self__訪問。
方法也支持訪問(但不能設置)底層函數對象的任何函數屬性。
用戶定義的方法對象可能在獲取類的一個屬性的時候創建(可能通過類的一個實例),如果那個屬性是用戶定義的函數對象或者一個未綁定的用戶方法對象或者一個類方法對象。如果那個屬性是用戶定義的方法對象,只有類和存儲在原始方法對象中的類或其子類相同,才會創建一個新的方法對象;否則,使用初始的方法對象。
如果用戶定義的方法是通過從類中獲取用戶定義的方法創建,它的im_self為None并且方法對象稱為
- Python 2 教程
- 1. 吊吊你的胃口
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限
- Python 2 標準庫
- 1. 引言
- 2. 內建函數
- 3. 不太重要的內建函數
- 4. 內建的常量
- 5. 內建的類型
- 6. 內建的異常
- 7. String Services
- 8. Data Types
- 9. Numeric and Mathematical Modules
- 10. File and Directory Access
- 11. Data Persistence
- 13. File Formats
- 14. Cryptographic Services
- 15. Generic Operating System Services
- 16. Optional Operating System Services
- 17. Interprocess Communication and Networking
- 18. Internet Data Handling
- 20. Internet Protocols and Support
- 26. Debugging and Profiling
- 28. Python Runtime Services
- Python 2 語言參考
- 1. 簡介
- 2. 詞法分析
- 3. 數據模型
- 4. 執行模型
- 5. 表達式
- 6. 簡單語句
- 7. 復合語句
- 8. 頂層的組件
- 9. 完整的語法規范
- Python 3 教程
- 1. 引言
- 2. Python 解釋器
- 3. Python簡介
- 4. 控制流
- 5. 數據結構
- 6. 模塊
- 7. 輸入和輸出
- 8. 錯誤和異常
- 9. 類
- 10. 標準庫概覽
- 11. 標準庫概覽 — 第II部分
- 12.現在怎么辦?
- 13. 交互式輸入的編輯和歷史記錄
- 14. 浮點數運算:問題和局限