前一節中,我們對Python中的對象有了一個基本的瞭解。從本節開始,我們將分別瞭解不同類型的對象,並且對不同對象的特點,特別是對象有關的函數進行介紹。
本節將會介紹數字類型,也就是Number類型,在Python中是一種不可變類型。
5.1 整數
Python能夠處理任意大小的整數,例如0,-1,9989080等等
除了用一般的十進制表示,也可以用二進制(0x)、八進制(0b)、十六進制表示(0o)。並且不同進制的轉換有相應的內置函數:
print("十進制數爲:", 5)
print("轉換爲二進制爲:", bin(5))
print("轉換爲八進制爲:", oct(5))
print("轉換爲十六進制爲:", hex(5))
執行結果爲:
十進制數爲:5
轉換爲二進制爲: 0b101
轉換爲八進制爲: 0o5
轉換爲十六進制爲: 0x5
5.2 浮點數
浮點數也就是小數,例如3.14,5.34,-0.3等等,也可以用科學計數法表示,用e來代替10,例如3.14x109 就要用3.14e9
來表示。
5.3 複數
Python中複數的表示與我們在課本中學到的一致,分爲實部和虛部,虛部末尾需要加j或J,例如4.2+5j
,43-0.9J
。需要注意的是,Python中複數的實部和虛部都是浮點數。
複數對象主要的兩個屬性就是real、imag,分別返回實部和虛部,還有一個conjugate方法,返回共軛複數
>>> num = 4.2+5j
>>>
>>> num.real
4.2
>>> num.imag
5.0
>>> num.conjugate()
(4.2-5j)
5.4 布爾型
在Python中布爾型嚴格來說也是整數的一種,布爾型只有兩個值,True
和False
,主要運用在邏輯運算上。
任意一個類型的對象都可以通過bool()
轉換爲對應的布爾值,實際程序中可能很少會用到這個函數,但是必須要牢記不同對象對應的布爾值是什麼,規則如下:
- 數字(int,float,complex)只要爲0,對應布爾值就是False,反之爲True
- 空的容器對象(空的str、list、tuple、dict、set)在Python中的布爾值都是 False、非空的爲True
- 沒有__nonzero__()方法的對象的默認值是True
5.5 運算符與重要的內置函數
瞭解了各種數字類型之後,接下來就是學習在程序中如何使用它們,這就涉及到各種不同的運算符以及內置函數。
5.5.1 算數運算符
算數運算符包括加減乘除(+,-,*,/,//),取餘(%),冪運算(**)。算數運算符的使用有以下幾個需要注意的要點
- 操作數類型不一
舉個例子,在使用加法的時候,用一個整數加小數是很常見的,譬如9.8+1
,但理論上,Python中使用+
時,左右兩個操作數應當是相同類型。之所以9.8+1
這個表達式依然可以正確地執行,是因爲Python使用了隱式的數字類型強制轉換機制來保證操作數類型一致。
強制轉換的機制可以參考《Python核心編程》中的描述:
如果有一個操作數是複數, 另一個操作數被轉換爲複數。
否則,如果有一個操作數是浮點數, 另一個操作數被轉換爲浮點數。
否則, 如果有一個操作數是長整數,則另一個操作數被轉換爲長整數;
否則,兩者必然都是普通整數,無須類型轉換。
強制轉換的規則很好理解,只要記住**精度低的操作數向精度較高的操作數看齊(有時會把精度更高稱作“更寬的類型”)**就可以了。例如9.8+1
,Python會把1
隱式地轉換爲浮點數。
強制轉換是一種隱式轉換,不需要程序員的參與,當然Python也提供了一些顯式的類型轉換函數,分別爲int(),float(),complex()
- true除法和floor除法
在Python2.2之前的版本中,表示除法的運算符只有/
這一個,它可以進行兩種類型的除法:
- floor除法:即“地板除”,兩個操作數都是整數,返回的是不大於正確結果的最大整數。有一些教程中會說截取了結果的小數部分,這種說法是恰當的,因爲結果爲負數的時候不符合這個規則:
#Python 2.7.13
>>> 1 / 2
0
>>> -1 / 2
-1 #結果是-1而不是0
- true除法:只要有一個操作數是浮點數,結果返回的就是浮點數:
#Python 2.7.13
>>> 1.0 / 2
0.5
>>> 1 / 2.0
0.5
>>> 1 / float(2)
0.5
在Python2.2之後,設計者們爲了使語言更加簡潔明瞭,就將floor除法的功能剝離出來,用//
來表示。這樣,無論操作數是整數還是浮點數,//
都將執行floor除法:
>>> 1 // 2
0
>>> 1.0 // 2
0
>>> 1 // 2.0
0
不過,只要你使用的是Python2.x,你就會發現/
仍然可以表示true除法和floor除法。在Python3.x中,/
才變成了僅代表true除法:
#Python 3.x
>>> 1 / 2
0.5
>>> 1.0 / 2
0.5
>>> 1 / 2.0
0.5
如果你希望在Python2.x中讓/
只表示true除法,可以在程序中加入這樣一條語句from __future__ import division
。
- 取餘運算
取餘只需要記住一個要點:取餘過程中的商一定是整數,並且是通過floor除法得到的整數。
- 冪運算
冪運算**
需要注意優先級問題。
首先,一元操作符(+,-代表取正,取負)的優先級大於加減乘除和取餘(+,-,*,/,//,%)運算,可以用一個例子來理解:
>>> -1 // 2
-1
>>> -(1 // 2)
0
這裏的-
表示取負數,優先級一定大於//
,編寫程序的時候保險起見也可以加上括號,-1 // 2
其實也就等價於(-1) // 2
冪運算**
比較特殊,它會比左側操作數的一元運算符優先級高,這也就是說-1 ** 2
與(-1) ** 2
不等價:
>>> -1 ** 2 #冪運算先計算
-1
>>> (-1) ** 2
1
另外,除了運算符**
,Python還提供了內置函數pow()
用於乘方運算。
傳入兩個參數時,pow(x, y)
等價於x**y
。
傳入三個參數時,且x,y,z都爲整數時,pow(x, y, z)
與x ** y % z
等價,而當x,y,z有一個不是整數時,pow(x, y, z)
會報錯:
>>> pow(3, 6, 6.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pow() 3rd argument not allowed unless all arguments are integers
此外,math模塊中還提供了sqrt()函數,用於開平方,不過完全可以用pow()來代替:
from math import * #爲了使用sqrt()必須導入math模塊,pow則不用
>>> sqrt(8)
2.8284271247461903
>>> pow(8,0.5) #開平方
2.8284271247461903
>>> pow(8,1/3) #開三次方
2.0
5.5.2 比較運算符
比較運算符包括==,!=,>,<,>=,<=
,運算返回的結果是bool值(True
或False
),常用在條件判斷中,例如if a != b:…
5.5.3 邏輯運算符
邏輯運算符包括and,or,not
,分別對應與、或、非。也常用在條件判斷中,例如if a and b:…
if not b:…
注意Python中邏輯運算不會直接返回bool值(True
或False
),實際規則如下:
邏輯表達式 | 返回規則 |
---|---|
x and y | 如果 bool(x) 爲 False,x and y 返回 x,否則返回 y 。 |
x or y | 如果 bool(x) 爲 True,x and y 返回 x,否則返回 y 。 |
not x | 如果 bool(x) 爲 False,not x 返回 True,否則返回 False 。 |
5.5.4 成員運算符
成員運算符包括in,not in
,可以用於判斷特定的對象是否存在於序列中中,運算返回的結果是bool值(True
或False
)
>>> 1 in [1, 2, 3]
True
>>> 'd' in "abc"
False
in
一般會與for
連用,用於遍歷序列,例如下面這個例子:
#判斷list_1的哪些字符存在於list_2中
list_1 = ['A','B','C','D']
list_2 = ['A','B']
for letter in list_1:
if letter in list_2:
print(letter+' is exists in list_2')
else:
print(letter+' is not exists in list_2')
運行結果:
A is exists in list_2
B is exists in list_2
C is not exists in list_2
D is not exists in list_2
5.5.5 身份運算符
身份運算符包括is,is not
,用於判斷兩個變量是否引用自同一個對象,因此,a is b
也就等價於 id(a) == id(b)
。
5.5.6 賦值運算符
賦值運算在第二章中已經作爲基本語法介紹過,主要包括的就是普通的賦值運算=
,以及結合算術運算符的增量運算+=,-=,*=,/=,//=,%=,**=
。
5.5.7 位運算符
還有一個初學時不常用,但實際上非常實用的運算符,就是位運算符。位運算中包含運算符&,|,^,~,<<,>>
首先要記住的是,位運算的操作數都是整數,並且運算針對的是整數的二進制形式。
先介紹&,|,^
這三個運算符,它們分別被稱爲“按位與”、“按位或”、“按位異或”。先說&
,實際上就是把左右兩個操作數的二進制形式逐位進行與運算,得到一個新的二進制數,用一個例子來理解:
>>> a = 60 # 60 = 0011 1100
>>> b = 13 # 13 = 0000 1101
>>> a & b
12 # 12 = 0000 1100
以此類推,|、^
就是將每一位上的與運算改爲或運算和異或運算即可。
~
運算符的作用是“按位取反”,顧名思義,就是將二進制的每一位1變爲0,0變爲1。
<<,>>
是左移運算符和右移運算符,用於將運算數的各二進位全部左移或右移若干位
>>> a = 13 # 13 = 0000 1101
>>> a << 1
26 # 26 = 0001 1010
>>> a << 2
52 # 52 = 0011 0100
>>> a >> 1
6 # 6 = 0000 0110
除了各類運算符之外,還有一些針對數字類型對象的內置函數需要了解
5.5.8 舍入函數
- floor(x)
floor代表地板,那麼顧名思義,就是在傳入參數的“下面”,即取不大於傳入參數的最大整數:
>>> floor(1.3)
1
>>> floor(-1.3)
-2
- ceil(x)
ceil代表房頂,就是在傳入參數的“上面”,即取不小於傳入參數的最小整數:
>>> ceil(1.3)
2
>>> ceil(-1.3)
-1
- round(x, [, n] ) (這裏x不能缺省,代表目標操作數,而n是可以缺省的,代表保留幾位小數,缺省爲0)
人們有時候會說round()的作用是四捨五入:
>>> round(8.2)
8
>>> round(-9.6)
-10
round()也可以指定保留幾位小數:
>>> round(8.5)
8
>>> round(56.659, 1)
56.7
不過在使用的時候,可能會發現一些問題:
在python2.x中運行:
>>> round(2.5)
3.0
>>> round(-2.5)
-3.0
在python3.x中運行:
>>> round(2.5)
2
>>> round(-2.5)
-2
>>> round(3.5)
4
>>> round(-3.5)
-4
因此準確來說,round()一方面可以完成的是四捨六入的功能;另一方面,當傳入的數字與兩側整數距離相同的時候,python2.x的做法是:保留到離0較遠的一側,而python3.x的做法是:保留到偶數的一側。
需要注意的是,上述規則適用於傳入的數字與兩側整數距離相同的時候,那麼按理來說,無論python2.x還是python3.x,round(2.675, 2)
的結果都應該是2.68
,但是實際上結果卻是2.67
!這主要是因爲2.675轉換爲二進制之後,小數點後面的位數太多了(大約是10.10101100110011001100110011001100110011001100……),超出了計算機能夠表示的精度,因此小數點後面的一串二進制數已經被截斷了一部分,因此2.675在計算機中並不能精確表示,而是離2.67更近一些,使用round()時直接舍到2.67即可。
可見,round()結果會受到計算機本身表示精度的影響,因此可能會帶來一些意想不到的結果,如果對精度要求高的計算,儘量避免使用round()
,一些替代的方案可以參考這篇blog:
http://www.runoob.com/w3cnote/python-round-func-note.html
5.5.9 random模塊
random模塊中包含了多個生成隨機數對象的函數,是Python中的一個非常常用的模塊。
在使用random模塊前需要導入:
import random
這裏提一個代碼風格上的問題。我們在之前導入math模塊的時候使用的是from math import *
同樣我們可以使用import math
,區別在於前者我們可以直接調用模塊的內置函數,例如sqrt()
;而後者這樣導入,我們必須採用模塊.函數的格式調用內置函數:math.sqrt()
。Python的技術規格中更建議使用後者,這是因爲不同模塊中可能有同名的函數,爲了避免混淆,在調用模塊內置函數的時候,最好還是在函數名前面加上模塊名。
回到random模塊,主要的幾個函數如下:
- random.random()
生成一個0到1之間的隨機浮點數,包括0但不包括1,也就是[0.0, 1.0)
>>> random.random()
0.02025527401777727
- random.randint(x, y)
隨機生成[x,y](包括x, y)區間內的整數,需要注意x,y都必須是整數,且x<=y。
>>> random.randint(1,9)
4
- random.uniform(x, y)
隨機生成 [x, y) 或[y,x)區間內的浮點數,注意x,y可以不是整數,並且不用考慮大小
>>> random.uniform(10.6,1.5)
9.664643121543374
- random.randrange ([start,] stop [,step])
該方法返回[start, stop),並且遞增基數爲step的集合內的一個隨機整數。start缺省值爲0,step缺省值爲1。注意start, stop, step都必須爲整數。
>>> random.randrange(1, 100, 2) #從1-99中選一個奇數
93
>>> random.randrange(100) #從0-99中選一個隨機整數
2
>>> random.randrange(0, 100, 5) #以5爲遞增基數,從0-99中選一個隨機數
45
以上四個函數用於生成隨機數,下面看一個能夠控制隨機數生成的函數
- random.seed([x])
傳入該函數一個固定的seed值,就能夠讓隨機數固定:
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
這其實也說明了random模塊中的函數生成的是僞隨機數,即生成隨機數的算法是固定的,改變的只是算法的參數seed值,如果不手動設定seed值,Python就會根據系統時間自行設定seed值。
接下來再看幾個與序列有關的函數,序列將會在之後的章節中詳細介紹,這裏提前接觸一下。Python中序列也就是三種:list(列表)、tuple(元組)、string(字符串)。
- random.choice(seq)
從序列中隨機選取一個元素,並返回這個元素,seq應當是一個序列,
>>> random.choice([1, 3, 4, 6, 9]) #list(列表)
1
>>> random.choice('a string') #string(字符串)
't'
>>> random.choice((1, 4, 5, 8)) #tuple(元組)
8
- random.sample(seq, n)
從序列中隨機選取n個元素形成一個新的list,並返回這個list,注意n不能大於原序列seq的長度。
>>> random.sample([1, 3, 4, 6, 9], 3)
[1, 9, 6]
>>> random.sample('a string', 3)
['g', 'i', 'a']
>>> random.sample((1, 4, 5, 8), 3)
[8, 1, 4]
需要注意,無論是random.choice()
還是random.sample()
,都不會改變傳入的原序列,而以下這個函數則不同。
- random.shuffle(seq)
該函數可以將序列seq中的元素打亂,random.shuffle()
沒有返回值,它是直接改變原序列seq。
>>> list_1 = [1, 3, 4, 6, 9]
>>> random.shuffle(list_1) ##本身沒有返回值
>>> list_1
[9, 1, 4, 6, 3]
>>> print(random.shuffle(list_1)) ##再次證明本身沒有返回值
None
本章的內容較多,梳理一下結構: