探索 Python(2): 探索 Python 型別的層次結構 —— 瞭解物件和容器

發表於2015-10-23

在 Python 語言中,所有事物都是程式可以訪問的物件, 其中包括儲存整數的簡單型別,以及您編寫的實際程式碼和這些程式碼在 Python 直譯器中的表示。對於熟悉其他程式語言的人來說,此行為可能導致某些混亂。但是,在實踐中,不會出現這種情況。Python 有一個良好定義的型別(或物件)層次結構。該層次結構在概念上可以劃分為四種類別:簡單型別、容器型別、程式碼型別 和內部型別。這四種類別和簡單型別本身在本系列的第一篇文章“探索 Python(1): Python 的內建數值型別”中作了介紹。本文將再次檢視可以在 Python 中使用的那些簡單的內建資料型別,這次著重介紹這些型別的物件屬性。然後,我們將介紹容器 型別的概念,並且將 Python tuple 類作為此型別的第一個示例,進行重點介紹。

簡單型別

內建到 Python 程式語言中的簡單資料型別包括:

  • bool
  • int
  • float
  • complex

支援簡單資料型別不是 Python 獨有的功能,因為多數現代程式語言都具有完整型別補充。例如 Java™ 語言甚至有一組更豐富的原始資料型別:

  • byte
  • short
  • int
  • long
  • float
  • double
  • char
  • boolean

但是,在 Python 中,簡單資料型別並不是原始資料型別,而是完善的物件,它們有自已的方法和類。另外,這些簡單的內建型別是不可改變的,這意味著:建立物件之後,您無法更改物件的值。如果需要新值,則必須建立新的物件。Python 簡單資料型別的不可改變特性與其他多數流行語言(如 Java 語言)處理簡單原始型別的方式不同。但是,當您對這些簡單資料型別的物件屬性有了更多的瞭解之後,就很容易理解這種差異。

所以,整數如何能夠擁有一些方法?它僅僅是一個數字嗎?不是的,至少在 Python 中答案是否定的。您自已可以對它進行檢驗:僅藉助內建的help 方法,就可以向 Python 直譯器諮詢關於 int 物件的資訊(參見清單 1 )。

清單 1. Python 直譯器: 用於整數物件的 Help

這具體說明了什麼?只有一個事情,那就是可以方便地從 Python 直譯器中得到幫助,但是從後面部分可以獲得更多幫助。第一行告訴您正在檢視 int 類的幫助頁面,它是一個內建的資料型別。如果您對物件導向的程式設計的概念不太熟悉,那麼可以將 想像成只是一個用於構建特殊事物並與之互動的藍圖。好比房子的設計藍圖,不僅顯示如何構建房子,還顯示房子完工之後,如何更好地使用房子。例如,設計圖會顯示不同房間的位置、在房間之間的移動方式以及出入房子的通道情況。

第一行的下面是對實際 int 類的詳細說明。在這一點上,您可能不熟悉如何在 Python 中建立類,因為顯示的語法類似於外語。沒關係,我將在另一篇文章中對此進行全面介紹。現在,您只需要知道:int 物件是從 object 類中繼承而來,它是 Python 中許多內容的一個基類。

後面的幾行介紹 int 類的建構函式。建構函式 只是建立特定類例項(或物件) 的特殊方法。建構函式方法好比建築承包人,它利用房子的設計圖建房子。在 Python 中,建構函式的名稱與其建立的類的名稱相同。類可以有不同的建構函式方法,這些方法是通過類名稱後的圓括號內附帶的不同屬性進行區分。類可以有不同建構函式方法的較好的一個例子就是 int 類, 實際上,您可以用多種方法呼叫它,具體採用哪種方法取決於圓括號中放置的引數(參見清單 2)。

清單 2. Python 直譯器:int 類建構函式

這四個建構函式呼叫建立了四個不同的整數。第一個建構函式建立了一個整數物件,其值為 0,在沒有值提供給 int 類建構函式的情況下,該值是所使用的預設值。第二個建構函式根據規定建立了一個值為 100 的整數。第三個建構函式採用了字串“100”並建立了以 10 為基數的整數值(常見的十進位制系統)。最後一個建構函式也採用了字串“100”—— 但是它使用基數 8 來建立整數值,通常稱為 八進位制。不過,該值在輸出時會被轉換成十進位制數值,這就是該數字顯示為 64 的原因。

您可能想知道如果省略了建構函式呼叫中的圓括號將會發生什麼。在這種情況下,您可以向該變數分配一個實際的類名稱,有效地為原先的類建立一個別名(參見清單 3)。

清單 3. Python 直譯器:int 型別

真是太棒了!您立即可以建立一個由內建 int 類定義的新資料型別。但請注意不好的一面,不要濫用這一新功能。優秀的程式設計師除了使程式碼具有良好效能外,還應努力使程式碼清淅。這類編碼技巧的確有其使用價值,但它們並不常見。

使用 Python 直譯器可以使新的 Python 程式設計師簡化學習過程,少走彎路。如果您想詳細瞭解 Python 內的 help 工具,只需在 Python 直譯器中的命令提示符下鍵入 help() ,就可以訪問互動式的幫助工具(參見清單 4)。

清單 4. Python 直譯器:幫助直譯器

您可能已經對此有所瞭解,但在 help> 提示符處輸入 int 可以顯示那些為以前的 int 類顯示的類描述。

容器型別

到目前為止,已經談論了許多 Python 語言中使用的簡單型別。但是多數程式並不簡單,它們涉及通常由簡單型別組成的複雜資料。因此,現在的問題就成了“如何在 Python 中處理複雜資料?”

如果您熟悉物件導向的語言,如 Java 或 C#,那麼您可能認為該問題的答案很簡單:只需建立一個新類來處理複雜的資料即可。該方法也適用於 Python,原因是 Python 支援通過類建立新型別。但是,在多數情況下,Python 還可以提供更為簡單的方法。當您的程式需要一次處理多個物件時,就可以利用 Python 容器類:

  • tuple
  • string
  • unicode
  • list
  • set
  • frozenset
  • dictionary

這些容器型別提供了兩種功能。前六個型別是有序的,最後一個型別 dictionary 則是一個對映。有序型別與對映型別的區別較為簡單。有序型別 僅僅是指物件的順序。所有的有序型別(除 set 和 frozenset 型別外)都支援訪問給定順序的物件。相比之下,對映容器 則用於儲存那些對順序不是很敏感的物件;通過提供可以找到關係值的金鑰,就可以從容器中提取值。

容器型別間的另一個不同點來自於它們所持有的資料的特性,下面四種容器型別的順序是不可變的:

  • tuple
  • string
  • unicode
  • frozenset

這意味著在您建立了這些容器型別之一後,所儲存的資料就不可更改。如果出於某種原因需要更改資料,則需要建立一個新容器來儲存新的資料。

後三種容器型別(listset 和 dictionary)都是可變容器,因此,它們可以根據需要更改儲存的任何資料(但在 dictionary 中所使用的金鑰是不可變的,就像您房間的鑰匙)。雖然可變容器非常靈活,但它們的動態特性會對效能造成影響。例如,tuple 型別,儘管它是不可變的,靈活性較差,但在同一環境中使用時,它們通常比 list 型別快得多。

這些容器類提供了強大的功能,它們通常是多數 Python 程式的核心。本文的其餘部分討論了 tuple 型別,它用於引入許多與建立和使用 Python 中的容器型別有關的基本概念。其餘的型別將在以後的文章中討論。

元組

tuple 型別像一個口袋,在出門前可以把所需的任何東西一股腦地放在裡面。您可以將鑰匙、駕駛證、便箋簿和鋼筆放在口袋裡,您的口袋是存放各種東西的收集箱。Python 的 tuple 型別與口袋類似,它可以存放不同型別的物件。您只需向變數分配一個用逗號分隔的物件序列,就可以建立一個 tuple(參見清單 5)。

清單 5. Python 直譯器:建立一個 tuple

該示例程式碼顯示瞭如何以多種方式建立 tuple。第一種方法是建立一個包含從 0 到 9 整數序列的 tuple。第二種方法與第一種相同,但這次省去了括號。在建立一個 tuple 時,括號通常是可選的,但有時是必需的,這取決於上下文。結果,您會習慣性地使用括號來減少混淆。最後一個 tupletc 使用了一個實際的類建構函式來建立 tuple。這裡重要的一點是,建構函式構成中僅有一個變數,因此您必須在括號中包括物件序列。最後兩個建構函式呼叫演示瞭如何通過在括號內不放任何東西來建立空的 tuple (et),以及如何通過將一個逗號放在序列中僅有的一個專案後面來建立 tuple (st)。

使用口袋裝東西的一個主要原因是為了方便生活。但要求在需要這些東西的時候能夠迅速地從口袋中取出它們。Python 中的多數容器型別(其中包括 tuple)允許您使用方括號操作符從集合中方便地訪問資料項。但 Python 比其他語言更具靈活性:您可以使用通常稱為分段 的方法選擇一個專案或多個有序專案(參見清單 6)。

清單 6. Python 直譯器:從 tuple 訪問專案

在建立簡單的 tuple 之後,前面的示例顯示如何選擇一個資料項 —— 在本示例中是整數 2。這時,請注意 Python 使用了零排序,其中集合中的專案從零開始編號。如果您熟悉使用 Java 語言、C# 或其他從 C 語言派生的語言進行程式設計,那麼您應該非常熟悉此行為。否則,該概念也是非常簡單的。用於訪問資料項的索引只宣告集合中越過第一個資料項有多遠,或者稱為序列,您需要去獲得所需的內容。因此,要獲得第三個資料項(在本示例中為整數 2),您需要從第一個資料項起越過兩個資料項。在訪問第三個資料項時,Python 知道它是一個整數物件。您還可以方便地從集合中提取多個資料項。在本示例中,您建立了一個新的 tuple,其值為從最初的 tuple 開始第一、第二和第十個值。

其餘的示例顯示瞭如何使用 Python 的分段功能從序列中一次選擇多個資料項。術語分段 是指從序列中對資料項進行分段的方法。分段的工作方式是宣告開始索引、結束索引和一個可選的步驟大小,全部都用分號分隔。因此,t[2:7] 將 tuple 中的第三到第七個資料項分段,而t[2:7:2] 則對每兩個資料項進行分段,從 tuple 中的第三個資料項開始一直到第七個資料項。

我目前建立的 tuple 物件是同類的,它們僅包含整數物件。所幸的是,tuple 要比顯示的示例複雜得多,因為 tuple 實際上是一個異構容器(參見清單 7)。

清單 7. Python 直譯器:異構的 tuple

您會看到,建立可以擁有各種型別資料項(其中包括另一 tuple)的 tuple 是多麼方便。並且可以使用方括號操作符以相同的方式訪問所有資料項,它支援將不同型別的有序資料項分段。然而,tuple 是不可變的。因此,當我嘗試更改第五個元素時,發現不允許對資料項分配。打一個簡單的比方,在您將某些東西放入口袋後,改變所取東西的惟一方式是取一個新口袋,並將所有資料項放進去。

如果需要在現有 tuple 中建立一個包含資料項子集的新 tuple,最簡單的方法是使用相關的片段,並根據需要同時新增子集(參見清單 8)。

清單 8. Python 直譯器:使用 tuple

您還可以將現有 tuple 的片段與新 tuple 的片段合併在一起。使用片段語法,無需指定開始或結束索引,就可以製作現有 tuple 的副本。最後兩個示例也非常有趣。內建的 len 方法告訴您 tuple 中資料項的數量。從巢狀的 tuple 訪問資料項也非常簡單:選擇巢狀的 tuple,然後從中訪問有趣的資料項。

您還可以從稱為打包 的過程的一組現有變數中建立一個tuple。反之亦然,其中,tuple 中的值被指派給變數。這之後的過程稱為解包,它是用於許多情形的功能十分強大的技術,其中包括希望從一個函式中返回多個值。在解包 tuple 時,僅有的問題是必須為 tuple 中的每個資料項提供一個變數(參見清單 9)。

清單 9. Python 直譯器:打包和解包 tuple

簡化概念

儘管看上去十分複雜,但 Python 的物件屬性實際上簡化了 Python 語言新手常常面臨的一些更為複雜的概念。在瞭解如何使用物件之後,所有東西都是物件這一概念意味著您已經進一步理解了一些新概念。如 Python 的容器型別。使困難的任務變得簡單化是使用 Python 得到的常見好處之一;另一個例子是內建的幫助工具,只需在 Python 提示符處輸入 help(),就可以在 Python 直譯器中看到該工具。由於生活不是用一些簡單的概念描述的,所以 Python 提供了一組豐富的容器(即集合)物件。在本文中,我介紹了其中的最簡單的物件 —— tuple。要正確使用tuple,就需要熟悉它的工作方式。但是,由於許多其他容器型別具有類似的功能,其中包括分段以及打包或解包,瞭解 tuple 的工作原理意味著您已經開始完全理解 Python 中的其他容器型別。

相關文章