Python基礎資料型別
(1)整型int
基本資料型別的值都可通過字面值(literal)的形式表示出來,即以字面形式表現值。 整數型別的字面值表示形式和我們在現實世界中的寫法一樣,例如下列都是合法的整數:
123 -456 0
注意,整數字面值是不能包含小數點的,即使小數點後面什麼都沒有!讀者也許會覺得 這句話很奇怪,因為在數學中從沒見過一個數包含小數點但小數點後面啥也沒有的情形。然 而,在 Python 中確實允許以下形式的字面值:
123. -456. 0.
但它們都不是整數!事實上,以上三個數分別等於 123.0、-456.0 和 0.0,它們屬於後 文即將介紹的浮點數型別。
Python 語言為整數型別提供了通常的數學運算,運算子及其含義如下表所示:
運算子 | 含義 |
---|---|
+ | 加 |
- | 減 |
* | 乘 |
/ | 除 |
** | 乘方 |
% | 取餘數 |
abs() | 取絕對值 |
例如:
>>> 23 + 45
68
>>> 56 – 12
44
>>> 8 * 2
16
>>> 11 / 3
3
>>> 8 ** 2
64
>>> 18 % 5
3
>>> abs(-8)
8
可見,計算機實現的整數運算基本上和我們在數學課上所學的一樣,除了一個例外—— 除法。由於例中的 11/3 是整數型別上的除法,運算結果仍然在整數型別當中,所以 Python 將商的小數部分直接捨棄了(未作四捨五入!),從而結果為 3。在程式中,本來希望得到精 確的除法結果,但因被除數和除數都是整數,導致結果誤差過大甚至出錯,這是初學 Python 程式設計的人很容易防錯誤的地方。要說明一下,表 2.1 中的 abs()並不是運算子,而是 Python 的內建函式,這裡只是為了方便而將它列在了表中。
除了上面這些運算子,Python 還提供了一些運算子與變數賦值結合起來的表示法。例 如,在程式設計中經常用到一個變數遞增的操作:x = x + 1。注意,這個式子在數學中 是不成立的,因為一個數不可能“等於”該數加 1。但在程式語言中這是一個完全合法的賦 值語句,它的含義是:將變數 x 所指向的值加 1,並將計算結果重新賦值給 x。鑑於這個操 作頻繁使用,Python 和某些其他語言提供了一種簡寫形式:x += 1。請看例子:
>>> x = 123
>>> x += 1
>>> print x
124
還有其他一些類似的簡寫形式,參見下表
普通形式 | 簡寫形式 |
---|---|
x = x + y | x += y |
x = x - y | x -= y |
x = x * y | x *= y |
x = x / y | x /= y |
x = x % y | x %= y |
賦值與運算結合
int 型別的侷限性
計算思維是建立在計算機的能力和限制之上的。現在我們來討論整數型別的一個限制。
int 型別只是數學中的整數集合 I 在計算機中的表示,而一個事物和該事物的一種表示 之間未必可以劃等號。事實上,型別 int 只表示了 I 的一個子集,I 是無窮集合,而 int 是有窮的。這是為什麼呢?
在計算機底層,整數一般都是用特定長度的二進位制數表示的。至於具體長度是多少,取 決於 CPU 的設計。目前個人計算機上多采用 32 個二進位制位(bit,位元)的長度來表示整數, 故 Python 語言中的 int 型別就是 32 位元長度的整數值。利用一點排列組合知識,容易推 知:一個位元有兩種可能的狀態(0、1),兩個位元有四種可能的狀態(00、01、10、11), 三個位元有八種狀態(000、001、010、011、100、101、110、111),…,32 個位元有 232種可能的狀態。用這 232 種狀態顯然只能表示 232 個整數,考慮到整數有正負,計算機底層 將這 232 個狀態的一半用於表示非負整數,另一半用於表示負整數,從而型別 int 實際上是 由-231~231-1 之間的所有整數構成的集合①。
我們已經瞭解,資料是現實世界資訊在計算機中的抽象,根據資料值的種類和操作的不 同而劃分成不同資料型別。一般來說在邏輯層次上理解和使用資料型別就夠了,不需要進一 步瞭解這些抽象在計算機底層的物理表示。然而,如果能對資料型別的底層表示方法有所瞭解,可以使資料和程式設計更好地建立在機器的能力和限制之上。
① 有的語言還支援用 32 位元表示 0~232-1 的無符號整數。
(2)浮點型float
浮點數就是包含小數點的數,大體對應於數學中的實數集合。現實世界中的職工工資(以 元為單位)、房屋面積(以平方米為單位)、人的身高(以米為單位)、圓周率等在程式中都 適合用浮點數表示。
Python 語言提供了型別 float 用於表示浮點數。float 型別的字面值形式與數學中的 寫法基本一致,但是允許小數點後面沒有任何數字(表示小數部分為 0),例如下列字面值 都是浮點數:
3.1415 -6.78 123.0 0\. -6.
Python 為浮點數型別提供了通常的加減乘除等運算,運算子與整數型別是一樣的。但是,與整數型別不同的是,運算子“/”用於浮點數時,是要保留小數部分的, 例如:
>>> 11.0 / 3.0
3.6666666666666665
沒錯,最後一位小數是 5 而不是 6!原因見下面關於浮點數內部表示的內容。 將一個浮點數賦值給變數,則該變數就是 float 型別(實際上是指向一個 float 型別的資料)。例如:
>>> f = 3.14
>>> type(f)
<type 'float'>
浮點數運算同樣可以和變數賦值結合起來
浮點數的能力與限制 浮點數型別能夠表示巨大的數值,能夠進行高精度的計算。但是,由於浮點數在計算機
內是用固定長度的二進位制表示的,有些數可能無法精確地表示,只能儲存帶有微小誤差的近 似值。例如,
>>> 1.2 – 1.0
0.19999999999999996
結果比 0.2 略小。又如:
>>> 2.2 – 1.2
1.0000000000000002
結果比 1.0 略大。然而,下面這個表示式卻計算出了精確結果:
>>> 2.0 – 1.0
1.0
儘管浮點表示帶來的這種微小誤差不至於影響數值計算實際應用,但在程式設計中仍然 可能導致錯誤。例如,萬一某個程式中需要比較 2.2 ? 1 是否等於 1.2,那我們就得不到 預期的肯定回答,因為 Python 的計算結果是不相等!請看下面兩個比較式:
>>> (1.2 – 1.0) == 0.2
False
>>> (2.0 – 1.0) == 1.0
True
先解釋一下,上例中用到了比較兩個表示式是否相等的運算子“==”,另外顯示結果出 現了表示真假的布林值 True 和 False,這些內容在後面布林型別一節中有詳細介紹。從 這個例子我們得到一條重要的經驗:不要對浮點數使用==來判斷是否相等。正確的做法是 檢查兩個浮點數的差是否足夠小,是則認為相等。例如:
>>> epsilon = 0.0000000000001
>>> abs((1.2 – 1.0) - 0.2) < epsilon
True
另外從運算效率考慮,與整數型別 int 相比,浮點數型別 float 的運算效率較低,由 此我們得出另一條經驗:如果不是必須用到小數,那就應當使用整數型別。
科學記數法
對於很大或很小的浮點數,Python 會自動以科學記數法來表示。所謂科學記數法就是
以“a×10 的整數次冪”的形式來表示數值,其中 1 <= abs(a) < 10。例如,12345 可 以表示成 1.2345e+4,0.00123 可以表示為 1.2345e-3。下面是 Python 的計算例子:
>>> 1234.5678 ** 9
6.662458388479362e+27
>>> 1234.5678 ** -9
1.5009474606688535e-28
正如 int 不同於整數集 I 一樣,Python 的 float 也不同於實數集 R,因為 float 仍 然只能表示有限的浮點數。當一個表示式的結果超出了浮點數表示範圍的時候,Python 會 顯示結果為 inf(無窮大)或-inf(負無窮)。讀者可以做一個有趣但略顯麻煩的實驗,試 一試 Python 最大能表示多大的浮點數。下面是本書著者所做的實驗結果,可以看到,最大 浮點數的數量級是 10308,有效數字部分已經精確到小數點後面第 53 位(Python 在顯示結果 時只保留小數點後 16 位),當該位為 6 時是合法的浮點數,當該位為 7 時則超出範圍。
>>> 1.79769313486231580793728971405303415079934132710037826e+308
1.7976931348623157e+308
>>> 1.79769313486231580793728971405303415079934132710037827e+308
inf
順便說一下,如果讀者做這個實驗,相信你一定會採用一種快速有效的策略來確定每一 位有效數字,而不會對每一位都從 0 試到 9。例如,當發現 1.7…1e+308 是合法的浮點數, 而 1.7…9e+308 超出了範圍,接下去應當檢查 1.7…5e+308 的合法性。這種方法就是本 書後面演算法設計一章中介紹的二分查詢策略。我們在第 1 章說過,計算思維人人皆有、處處 可見,不是嗎?
自動型別轉換
float 型別與 float 型別的資料相互運算,結果當然是 float 型別。問題是 float 型別能與 int 或 long 型別進行運算嗎?
由於整數、長整數和浮點數都是數值(在數學上都屬於實數集合 R),因此 Python 允許它們混合運算,就像 int 可以與 long 混合運算一樣。Python 在對混合型別的表示式進行 求值時,首先將 int 或 long 型別轉換成 float,然後再執行 float 運算,結果為 float 型別。例如:
>>> type(2 + 3.0)
<type 'float'>
>>> type(2 + 3L * 4.5)
<type 'float'>
手動型別轉換
除了在計算混合型別的表示式時 Python 自動進行型別轉換之外,有時我們還需要自己 手動轉換型別。這是通過幾個型別函式 int()、long()和 float()實現的。例如,當我 們要計算一批整型資料的平均值,程式中一般會先求出這批資料的總和 sum,然後再除以數 據的個數 n,即:
average = sum / n
但這個結果未必如我們所願,因為 sum 和 n 都是整數,Python 執行的是整數除法,小數部 分被捨棄了,導致結果誤差太大。為解決此問題,我們需要手動轉換資料型別:
average = float(sum) / n
其中 float()函式將 int 型別的 sum 轉換成了 float 型別,而 n 無需轉換,因為 Python 在計算 float 與 int 混合的表示式時,會自動將 n 轉換成 float 型別。
要注意的是,下面這種轉換方式是錯誤的:
average = float(sum/n)
因為括號裡的算式先計算,得到的就是整除結果,然後再試圖轉換成 float 型別時,已經 為時已晚,小數部分已經丟失了。
其實,呼叫型別函式來手動轉換型別並不是好方法,我們有更簡單、更高效的做法。如 果已知的資料都是整數型別的,而我們又希望得到浮點型別的結果,那麼我們可以將表示式 涉及的某個整數或某一些整數加上小數點,小數點後面再加個 0,這樣整數運算就會變成浮 點運算。例如求兩個整數的平均值:
>>> x = 3
>>> y = 4
>>> z = (x + y) / 2.0
>>> z
3.5
例中我們人為地將資料個數 2 寫成了 2.0,這樣就使計算結果變成了 float 型別。 當然,在將浮點數轉換成整數型別時,就沒有這種簡便方法了,只能通過型別函式來轉換。例如:
>>> int(3.8)
3
>>> long(3.8)
3L
可見,float 型別轉換成 int 或 long 時,只是簡單地捨去小數部分,並沒有做四捨五入。 如果希望得到四捨五入的結果,一個小技巧是先為該值(正數)加上 0.5 再轉換。更一般 的方法是呼叫內建函式 round(),它專門用於將浮點數轉換成最接近的整數部分。不過舍 入後的結果仍然是 float,為了得到 int 型別的資料還需要再用 int()轉換。例如:
>>> round(3.14)
3.0
>>> round(-3.14)
-3.0
>>> round(3.5)
4.0
>>> round(-3.5)
-4.0
>>> int(round(-3.14))
-3
bool
判斷兩個數是否相等時,Python給我們返回了布林型別的結果,那麼什麼是布林型別呢?
布林是19世紀英國的數學家,他建立了命題代數,所謂的命題就是可以判斷命題真假的語句。在程式語言中,將真假兩個值組成了一個型別,即布林型別,真假值也稱為布林值,以真假為值的表示式稱為布林表示式,布林表示式在程式中的作用是根據條件的真假執行對應的語句。
Python在2.3版本之後就定義了布林型別bool,bool型別的兩個值為True和False。在2.3版本之前,Python用1和0來表示真、假,這個方法沿用至今。
>>> if 1:
... print('true')
... else:
... print('false')
...
true
布林表示式最常用的是判斷兩個表示式的數值大小關係的。
[表示式] [運算子] [表示式]
>>> 2 == 2
True
但布林表示式在判斷字串的時候就不那麼簡單了。
>>> 'cyberkid' == 'cyberkid'
True
>>> 'cyberkid' == 'CYBERKID'
False
在Python中,字串是按字典的順序進行比較的,也就是說是基於字母順序比較,而字母順序是按照ASCII編碼順序排列的。所以,不管是大小寫字母,標點符號,阿拉伯數字以及各種字元也是要按照ASCII編碼來確定大小。
>>> 3 > 3
False
>>> 3 > 2
True
>>> 2 * 2 > 2
True
>>> 'like' > 'lake'
True
那麼,我們怎麼檢視這些數字、字母、標點符號在ASCII編碼中的位置大小呢?我們可以通過ord函式來檢視。
>>> ord('1')
49
>>> ord('a')
97
>>> ord('A')
65
>>> ord(',')
44
>>> ord('<')
60
ord函式返回字元在ASCII中的位置序號。
當然,僅用簡單的布林表示式不足以滿足某些需求,將多個簡單的布林表示式用邏輯運算子連線起來組成複雜的布林表示式,回顧一下我們學過的邏輯運算子:and、or、not。
[布林表示式] and [布林表示式]
[布林表示式] or [布林表示式]
not [布林表示式]
在上圖中,P和Q是參加運算的布林表示式,T和F表示真假值。在and中,P和Q各有兩種可能的值,所以P、Q組合共有4中不同的值組合,每種組合在表中一行表示,後面一列是P and Q的值,從表中可知,只有當P、Q都為真,並且P and Q為真,整個表示式為真。
>>> 3 > 3 and 3 < 4
False
>>> 3 > 2 and 3 < 4
True
在or中,只有P or Q為假,且P、Q也為假,表示式為假,也就是說,只要其中一項為真,則表示式為真。
>>> 3 > 3 or 3 < 4
True
>>> 3 > 3 or 3 == 4 or 3 == 3
True
not的用法相對簡單。
>>> not 3 > 3
True
>>> not not 3 > 3
False
上例中,not not 3 > 3
語句相當於我們生活中的雙重否定為肯定。利用這三個邏輯運算子可以構建複雜的布林型表示式。在複雜的布林表示式中,同算數運算子一樣,誰先誰後計算成了問題,這就要牽扯到運算子的優先順序了,回顧一下上一小節中我們列出運算子的優先順序的圖,可以看到邏輯運算子的優先順序。
not > and > or
在此再介紹一種別的語言不支援的表示式形式。
>>> 3 > 2 < 4
True
>>> 3 > 2 < 4 == 4 != 5
True
雖然這在數學中常用,但我們仍不推薦這種方式,因為這不為大多數語言所接受,對於這類表示式,還是用邏輯運算子比較好。
小結:有時候適當的加括號,來改變原有的優先順序,就像數學運算中加括號改變計算順序一樣如計算:2 * (2 + 2)。
>>> print( 0 or not 0 or '')
True
>>> print( 0 or not 0 or '' and 1)
True
>>> print( 0 or not 0 or '' and 1 and 0)
True
>>> print( 0 or not 0 or '' and 1 and 0 and None)
True
>>> print( (0 or not 0 or '') and 1 and 0 and None)
0
>>> print( (0 or not 0 or '') and 1 and 0 or None)
None
>>> print( (0 or not 0 or '') and 1 and 0 or None)
None
通過上面的例子,我們可以發現,返回值是有一定的規律的。
如果用x、y表示任何表示式。
x and y
and中,如果x的值為false,則返回x的值,否則返回y的值。
>>> 1 and 0
0
>>> 0 and 1
0
or中,如果x的值為false,則返回y的值,否則返回x的值。
>>> 0 or 1
1
>>> 1 or 0
1
not中,如果x的值為false,則返True,否則返回False。
>>> x = 0
>>> not x
True
>>> y = 1
>>> not y
False
>>> not not y
True
注意:Python中,元素自帶布林值,也就是說,每個元素都有自己的布林值,我們可以通過bool函式來證明。
>>> bool(0)
False
>>> bool(1)
True
>>> bool(None)
False
>>> bool('')
False
>>> bool([])
False
>>> bool(-1)
True
由上例可以看到,在Python中,0、None、空為假,其餘為真。 注意:空包括,空的字串,空的容器型別。
(4)複數型別complex
Python用complex表示複數型別,但由於不常用,我們只做瞭解。
在數學中,任意數可表示為a + bi,a稱為實部,b稱為虛部;而Python中complex型別的表示方法為(a + bj)。
注意,Python中的complex型別的虛數符號用j表示,而不是數學中的i,在不會產生誤解的情況下,(a + bj)可以省略括號為a + bj。
complex型別也可以執行數學運算。
>>> c1 = 3 + 5j
>>> c2 = 2 + 4j
>>> c1 + c2
(5+9j)
>>> c1 - c2
(1+1j)
>>> c1 * c2
(-14+22j)
>>> c1 ** c2
(-0.5249747542492873+0.16891854983884866j)
>>> abs(c1)
5.830951894845301
需要注意的是,abs函式對複數的計算是返回複數的模數。 我們也可以通過c1.real()和c1.imag()來分別獲取c1的實數和虛數,結果都是float型別。
>>> c1 = 3 + 5j
>>> c2 = 2 + 4j
>>> c1.real
3.0
>>> c1.imag
5.0