跨平臺系列 cross-plateform 跨平臺應用程式-01-概覽 cross-plateform 跨平臺應用程式-02-有哪些主流技術棧? cross-plateform 跨平臺應用程式-03-如果只選擇一個框架,應該選擇哪一個? cross-plateform 跨平臺應用程式-04-Reac ...
數值類型基礎知識
在Python中,數值實際上是一種類型的分類,包括:
-
整數、浮點數
-
複數
-
小數:固定精度對象
-
分數:有理數對象
-
集合:帶有數值運算的集合體
-
布爾值:
True
,False
-
內置函數與塊:
round
,math
,random
等 -
表達式、無限制精度整數(很大的整數)、位運算、八進位、十二進位、二進位
-
第三方擴展:向量、庫、可視化、作圖等
其中整數、浮點數最為常見,因此先從它們講起。
數值字面量
字面量 | 解釋 |
---|---|
1234, -24, 0, 99999999999999 |
整數(無大小限制) |
1.23, 1., 3.14e-10, 4E210, 4.0e+210 |
浮點數 |
0o177, 0x9ff, 0b101010 |
Python 3.X中八進位、十六進位、二進位字面量 |
0177, 0o177, 0x9ff, 0b101010 |
Python 2.X中兩種八進位、十六進位、二進位字面量 |
3+4j, 3.0+4.0j, 3J |
複數字面量 |
set('spam'), {1, 2, 3, 4} |
集合 |
Decimal('1.0'), Fraction(1, 3) |
小數、分數擴展 |
bool(X), True, False |
布爾類型、字面量 |
註意:
-
浮點數帶小數點,或加上科學技術標誌
e/E
。如果字面量帶有上述描述,那麼將其識別為浮點數。 -
整數:在Python2中有32位整數和長整數(無限制長),Python3中只有整數,即無限長的整數。
-
十六進位、八進位和二進位字面量:八進位以
0o
或0O
開頭,十六進位以0x
開頭,二進位以0b
或0B
開頭。 -
複數:字面量位實部+虛部,虛部以
j
或J
結尾。
內置數值工具
處理數字的工具包括:
-
表達式運算符
-
內置數學函數
-
工具模塊
它們也有一些專用於特定類型的方法。
Python表達式運算符
表達式的定義:數字/變數與運算符相結合,在執行時計算為一個值。
Python所有的運算符表達式如下:
運算符 | 描述 |
---|---|
yield x |
生成器函數send 協議 |
lambda args: expression |
創造匿名函數(lambda表達式) |
x if y else z |
三元選擇表達式,如果y 為真則取x ,否則取z |
x or y |
邏輯或 |
x and y |
邏輯與 |
not x |
邏輯非 |
x in y , x not in y |
x 是/不是y 的成員 |
x is y , x is not y |
`對象是否同一(不止是值相等) |
x < y , x > y , x <= y , x >= y |
x 是否小於/大於/不大於/不小於y |
x == y , x != y |
x 等於/不等於y (和上面的is 不一樣) |
x | y |
如果是數字,則按位與;如果是集合,則取並集 |
x ^ y |
如果是數字,則按位異或;如果是集合,則取對稱差集 |
x & y |
如果是數字,則按位與;如果是集合,則取交集 |
x << y , x >> y |
將x 左移/右移y 位 |
x + y |
如果是數字,則加;如果是序列,則拼接 |
x - y |
如果是數字,則減;如果是集合,則取差集 |
x * y |
如果是數字,則乘;如果是序列,則重覆 |
x % y |
如果是數字,則取x 除以y 的餘數;否則是格式化 |
x / y , x // y |
除法、除法向下取整 |
-x , +x |
取負、取正 |
~x |
按位非(取反碼) |
x ** y |
x 的y 次冪 |
x[i] |
索引 |
x[i:j:k] |
分片 |
x(...) |
調用函數/方法/類等等 |
x.attr |
屬性引用 |
(...) |
元組、表達式、生成器表達式 |
[...] |
列表、列表推導 |
{...} |
字典、集合、字典與集合推導 |
混合運算遵循運算符優先順序
-
以上表為標準,運算符從上到下優先順序逐漸升高;
-
如果優先順序相同,則從左到右運算。
括弧分組子表達式
如果我們使用了括弧,就不用考慮優先順序,因為Python優先計算括弧內的子表達式,再計算整個表達式。
混合類型向上轉換
如果表達式中有複數,那麼結果就為複數;如果表達式中只有整數和浮點數,則結果為浮點數;如果表達式中只有整數,則結果為整數(除非有除法)。
我們也可以通過強制轉換的方法選擇想要的類型:
>>> int(3.1415)
3
>>> float(3)
3.0
預習:運算符重載和多態
我們在之前也看見,+
也適用於字元串拼接。這是因為,字元串類型對+
進行了重載。用戶定義的新類型也可以重載+
。
Python的這個特性叫多態。
數字的實際應用
變數與基礎表達式
變數是名稱,用於記錄程式中的信息。在Python中:
-
變數在第一次賦值時被創建
-
變數在表達式中被使用時,會被替換為它們的值
-
變數在表達式中使用之前,必須已經賦值
-
變數引用對象,不需要事先聲明
例:下麵的例子中,自動創建變數a
和b
:
>>> a = 3
>>> b = 4
在表達式中使用剛剛創建的變數,此時變數替換為它們的值:
>>> a + 1, a - 1
(4, 2)
>>> b * 3, b / 2
(12, 2.0)
>>> a % 2, b ** 2
(1, 16)
>>> 2 + 4.0, 2.0 ** b
(6.0, 16.0)
註意到,我們輸入了多個表達式並用逗號分隔它們,這樣Python對每個表達式分別求值,並將它們用元組的方式組合起來。
如果使用還沒創建的變數,則Python會報錯:
>>> c * 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'c' is not defined
Python解釋數值表達式的時候,計算的先後順序與數學上的優先順序一致(如先乘除後加減,如果有括弧先算括弧)
數值的顯示格式
在早一些版本,會出現:
>>> b / (2.0 + a)
0.80000000000000004
>>> print(b / (2.0 + a))
0.8
後面的版本已經解決了這個問題。我們也可以用其他方法顯示電腦中數字的位數(比如直接在交互界面輸入變數名,或者使用字元串格式化):
>>> num = 1 / 3.0
>>> '%e' % num
'3.333333e-01'
>>> '%4.2f' % num
'0.33'
>>> '{0:4.2f}'.format(num)
'0.33'
普通比較與鏈式比較
普通數值可以進行比較,並返回一個布爾值。
>>> 2.0 >= 1
True
>>> 2.0 != 2.0
False
但是,一次比較只能比較兩個數,因此Python運行我們把多個比較連接起來:
>>> X = 2
>>> Y = 4
>>> Z = 6
>>> X < Y < Z
True
>>> X < Y and Y < Z
True
實際上,前者更有效率,因為Python只計算一次Y。
比較鏈可以是任意形式,也可以是任意長度。
>>> X < Y > Z
False
>>> 1 < 2 < 3.0 < 4
True
如果需要判斷表達式是否相等,且用到浮點數,那麼對其額外處理(如判斷兩者的差的絕對值是否小於一個很小的數):
>>> 0.1 + 0.2 == 0.3
False
>>> 0.1 + 0.2
0.30000000000000004
除法、經典除法、向下取整除法和真除法
-
x / y
:在Python2.X中,整數取省去小數部分,浮點數保留餘項;在Python3.X中,無論用何種類型,都會變成真除法(即保留小數部分)。 -
x // y
:向下取整除法。
>>> 10 / 4
2.5
>>> 10 / 4.0
2.5
>>> 10 // 4
2
>>> 10 // 4.0
2.0
可見,//
依賴於操作數的類型。
支持兩個Python腳本
在Python 2.X中,要實現像Python 3.X的除法效果,需要從模塊__future__
導入division
。
向下取整除法 vs 截斷除法
除法//
是向下截斷除法。對浮點數採用向下截斷的函數是math.floor
,向零截斷的函數是math.trunc
,效果如下:
>>> import math
>>> math.floor(2.5)
2
>>> math.floor(-2.5)
-3
>>> math.trunc(2.5)
2
>>> math.trunc(-2.5)
-2
在Python中,截斷除法//
總是向下取整。
>>> 5 / 2, 5 / -2
(2.5, -2.5)
>>> 5 // 2, 5 // -2
(2, -3)
>>> 5 / 2.0, 5 / -2.0
(2.5, -2.5)
>>> 5 // 2.0, 5 // -2.0
(2.0, -3.0)
為什麼截斷很重要
Python 3.X中的非截斷行為會嚴重影響大量的Python 2.X程式。
整數精度
Python 3.X支持無限制的整數大小,但Python 2.X識別長整數時在末尾加一個L。這方便了高精度運算(在C/C++需要額外實現)。
複數
複數的尾碼為j
或J
,我們可以把實部非0的複數寫成實部與虛部相加的形式。比如,實部為2,虛部為-3的複數可以寫為2 + -3j
或2 - 3j
。
複數運算的例子:
>>> 1j * 1J
(-1+0j)
>>> 2 + 1j * 3
(2+3j)
>>> (2 + 1j) * 3
(6+3j)
複數允許訪問實部和虛部,並支持所有數學表達式和cmath
模塊中的數學函數。
十六進位、八進位和二進位:字面量與轉換
在Python中,編寫以0o
或0O
為開頭的字面量,則將其識別為八進位;以0x
或0X
為開頭的字面量,則將其識別為十六進位;以0b
或0B
為開頭的字面量,則將其識別為二進位。
>>> 0o1, 0o20, 0o377
(1, 16, 255)
>>> 0x01, 0X10, 0xFF
(1, 16, 255)
>>> 0b1, 0B10000, 0b11111111
(1, 16, 255)
Python預設用十進位顯示數值。如果要將一個數轉換為八進位、十六進位和二進位,可以分別用oct
、hex
、bin
。
>>> oct(64), hex(64), bin(64)
('0o100', '0x40', '0b1000000')
內置函數int
將一個(十進位、八進位、十六進位、二進位)數字字元串轉換為一個整數,第一個參數是要轉換的字元,第二個參數是要把這個數字字元串看作是幾進位,不填則預設為十進位。
>>> int('64'), int('100', 8), int('40', 16), int('1000000', 2)
(64, 64, 64, 64)
>>> int('0x40', 16), int('0b1000000', 2)
(64, 64)
eval
函數把字元串作為Python代碼運行。
字元串格式化方法和表達式把整數轉換為指定的字元串:
>>> '{0:o}, {1:x}, {2:b}'.format(64, 64, 64)
'100, 40, 1000000'
>>> '%o, %x, %x, %X' % (64, 64, 255, 255)
'100, 40, ff, FF'
註意,在Python3.X中用0開頭的數字表示八進位會引發錯誤。其次,八進位、十六進位和二進位都可以是任意長度的整數。
>>> X = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
>>> X
25711008708143844408671393477458601640355247900524685364822015
>>> oct(X)
'0o77777777777777777777777777777777777777777777777777777777777777777777'
>>> bin(X)
'0b111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'
按位操作
Python支持C語言中的大多數數學表達式,比如把整數作為二進位串處理的運算(位運算)。如:左移、右移、邏輯與/非/或/異或。
>>> x = 1 # 0001
>>> x << 2 # 0100
4
>>> x | 2 # 0001 | 0010 = 0011
3
>>> x & 1 # 0001 & 0001 = 0001
1
在位運算下,之前介紹的二進位、十六進位就十分有用(因為十六進位的每一位對應二進位的四位)。
Python引入了新的整數方法bit_length
,查詢二進位表示一個數字的值時至少需要的位數。
>>> X = 99
>>> bin(X), X.bit_length(), len(bin(X)) - 2
('0b1100011', 7, 7)
事實上,位運算在Python中不太重要。
其他內置數學工具
內置模塊math
:
>>> import math
>>> math.pi, math.e # 數學常數
(3.141592653589793, 2.718281828459045)
>>> math.sin(2 * math.pi / 180) # 三角函數
0.03489949670250097
>>> math.sqrt(144), math.sqrt(2) # 算術平方根
(12.0, 1.4142135623730951)
>>> pow(2, 4), 2 ** 4, 2.0 ** 4.0 # 冪運算
(16, 16, 16.0)
>>> abs(-42.0), sum((1, 2, 3, 4)) # 絕對值、和
(42.0, 10)
>>> min(3, 1, 2, 4), max(3, 1, 2, 4) # 序列的最小值、最大值
(1, 4)
模塊math
包含多種取整方式:
>>> math.floor(2.567), math.floor(-2.567) # 向下取整
(2, -3)
>>> math.trunc(2.567), math.trunc(-2.567) # 向零取整
(2, -2)
>>> int(2.567), int(-2.567)
(2, -2)
>>> round(2.567), round(2.467), round(2.567, 2) # 四捨五入
(3, 2, 2.57)
註意到,優先函數如sin
需要導入math
,有些則不用。因為這些函數是內置函數,位於隱秘的命名空間內,對應於Python的builtins
模塊。
隨機數random
也需要導入。它可以選擇在區間\([0, 1]\)的任意實數:
>>> import random
>>> random.random()
0.83057242993689
>>> random.random()
0.36589352300294087
>>> random.random()
0.9102553345613595
也可以在規定的範圍內選擇一個隨機整數:
>>> random.randint(1, 10)
3
>>> random.randint(1, 10)
9
>>> random.randint(1, 10)
8
還能夠從一個序列中隨機地選取一項,以及打亂元素:
>>> random.choice(['Life of Brian', 'Holy Grail', 'Meaning of Life'])
'Holy Grail'
>>> random.choice(['Life of Brian', 'Holy Grail', 'Meaning of Life'])
'Meaning of Life'
>>>
>>> suits = ['hearts', 'clubs', 'diamonds', 'spades']
>>> random.shuffle(suits)
>>> suits
['clubs', 'diamonds', 'spades', 'hearts']
>>> random.shuffle(suits)
>>> suits
['spades', 'clubs', 'hearts', 'diamonds']
其他數值類型
小數類型
小數對象的名稱是Decimal
,它們有固定的位數和小數點,它的精度是固定的。
基礎知識
用浮點數來進行運算容易丟失精度:
>>> 0.1 + 0.2
0.30000000000000004
使用了小數對象,則結果更精確:
>>> from decimal import Decimal
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')
我們用decimal
模塊中的Decimal
函數創建小數對象,傳入一個表示小數的字元串。當不同精度的小數組合,則按照精確度最高的小數給出結果:
>>> Decimal('0.1') + Decimal('0.10')
Decimal('0.20')
我們可以從一個浮點數創建小數對象,但它會產生預設龐大的小數位數。
>>> Decimal(0.1) + Decimal(0.2)
Decimal('0.3000000000000000166533453694')
設置全局小數精度
預設的小數精度是:小數部分保留28位有效數字。
>>> import decimal
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.1428571428571428571428571429')
我們可以這麼修改顯示小數部分有效數字的位數:
>>> decimal.getcontext().prec = 4
>>> decimal.Decimal(1) / decimal.Decimal(7)
Decimal('0.1429')
>>> decimal.Decimal(0.1) + decimal.Decimal(0.2) - decimal.Decimal(0.3)
Decimal('1.110E-17')
小數上下文管理器
我們可以把特定的精度要求放在一個環境里,在環境外還是預設精度:
>>> decimal.Decimal('1.00') / decimal.Decimal('3.00')
Decimal('0.3333333333333333333333333333')
>>> with decimal.localcontext() as ctx:
... ctx.prec = 2
... decimal.Decimal('1.00') / decimal.Decimal('3.00')
...
Decimal('0.33')
>>> decimal.Decimal('1.00') / decimal.Decimal('3.00')
Decimal('0.3333333333333333333333333333')
分數類型
分數基礎知識
向上面的Decimal
,分數也需要導入模塊fraction
,並通過兩個整數(第一個是分子,第二個是分母)作為參數構造:
>>> from fractions import Fraction
>>> x = Fraction(1, 3)
>>> y = Fraction(4, 6)
>>> x
Fraction(1, 3)
>>> y
Fraction(2, 3)
>>> print(y)
2/3
我們可以把分數用於表達式運算符:
>>> x + y
Fraction(1, 1)
>>> x - y
Fraction(-1, 3)
>>> x * y
Fraction(2, 9)
分數和小數的數值精度
分數和小數的運算都比浮點數更直觀、準確。而且,分數會自動簡化結果。
>>> Fraction(6, 12)
Fraction(1, 2)
分數轉換和混用類型
浮點數有一個方法,可以將本身轉化為分子分母組成的元組。
>>> (2.5).as_integer_ratio()
(5, 2)
然後轉換為分數,這裡*
表示將元組的每個元素分別作為函數的參數。
>>> f = 2.5
>>> z = Fraction(*f.as_integer_ratio())
>>> z
Fraction(5, 2)
>>> x = Fraction(1, 3)
>>> float(x)
0.3333333333333333
>>> float(z)
2.5
註意到,float()
可以將分數類型轉換為浮點數。Fraction
也有一種方法將浮點數轉換為分數。
>>> Fraction.from_float(1.75)
Fraction(7, 4)
分數加整數的結果是分數,分數加浮點數的結果是浮點數:
>>> x = Fraction(1, 3)
>>> x + 1
Fraction(4, 3)
>>> x + 1.0
1.3333333333333333
但是,如果浮點數不能精確地表達某些實數(如1/3),那麼轉換為分數結果不精確。
>>> 1.0 / 3
0.3333333333333333
>>> (1.0 / 3).as_integer_ratio()
(6004799503160661, 18014398509481984)
我們可以限制分母地最大值來簡化結果:
>>> a = Fraction(*(1.0 / 3).as_integer_ratio())
>>> a
Fraction(6004799503160661, 18014398509481984)
>>> a.limit_denominator(10)
Fraction(1, 3)
集合
集合的性質與數學中集合的性質一致。如:無序、沒有重覆元素。
集合基礎知識
我們可以用一個序列或可迭代對象來創建集合:
>>> x = set('abcde')
>>> y = set('bdxyz')
>>> x
{'d', 'a', 'c', 'b', 'e'}
集合的運算:
>>> x - y # 差集
{'a', 'c', 'e'}
>>> x | y # 並集
{'d', 'x', 'z', 'a', 'c', 'b', 'e', 'y'}
>>> x & y # 交集
{'d', 'b'}
>>> x ^ y # 對稱差
{'e', 'z', 'a', 'x', 'y', 'c'}
>>> x > y, x < y # 是否為子集
(False, False)
集合成員測試in
:元素是否在集合/列表/字元串內。
>>> 'e' in x
True
>>> 'e' in 'Camelot', 22 in [11, 22, 33]
(True, True)
集合操作還提供了與這些操作對應的方法:
>>> z = x.intersection(y) # 交集
>>> z
{'d', 'b'}
>>> z.add('SPAM') # 加入新的元素
>>> z
{'d', 'b', 'SPAM'}
>>> z.update(set(['X', 'Y'])) # 與集合合併(並集)
>>> z
{'Y', 'X', 'b', 'd', 'SPAM'}
>>> z.remove('b') # 刪除一個元素
>>> z
{'Y', 'X', 'd', 'SPAM'}
集合可以用於len
、for
操作,但不適用於索引、分片。
集合操作對應的方法對任何可迭代類型有效:
>>> S = set([1, 2, 3])
>>> S | set([3, 4])
{1, 2, 3, 4}
>>> S | [3, 4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'set' and 'list'
>>> S.union([3, 4])
{1, 2, 3, 4}
>>> S.union((3, 4))
{1, 2, 3, 4}
新舊版本的集合字面量
對於集合S = {1, 2, 3}
,新版本(Python2.7,Python3.X)這麼顯示:
>>> S
{1, 2, 3}
舊版本(Python 2.X)這麼顯示:
>>> S
set([1, 2, 3])
空集表示(因此字面量{}
的類型是字典):
>>> S - {1, 2, 3}
set()
不可變性限制與凍結集合
集合只能包含不變的對象類型。
>>> S = {1.23}
>>> S.add([1, 2, 3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> S.add({'a': 1})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>> S.add((1, 2, 3))
>>> S
{1.23, (1, 2, 3)}
我們可以調用內置的frozenset
,這種類型的集合不可改變。
Python 3.X和Python 2.7中的集合推導
集合推導會運行一個迴圈併在每次迭代時收集一個表達式的結果,通過一個迴圈變數訪問當前的迭代值以用於集合表達式中。
>>> {x ** 2 for x in [1, 2, 3, 4]}
{16, 1, 4, 9}
在這個表達式中,迴圈部分在右側,集合體表達式在左側(x ** 2)
。意思是:對於列表[1, 2, 3, 4]
中的每一個x
,給出包含x
平方的一個新集合。推導式也適用於迭代其他類型的對象(如字元串)。
為什麼使用集合
去除集合體中的重覆
>>> L = [1, 1, 4, 5, 1, 4]
>>> set(L)
{1, 4, 5}
>>> list(set(L))
[1, 4, 5]
提取可迭代對象的差異,藉助集合完成順序無關的等價性測試。
>>> set(dir(bytes)) - set(dir(bytearray))
{'__bytes__', '__getnewargs__'}
>>> set(dir(bytearray)) - set(dir(bytes))
{'insert', 'reverse', '__delitem__', 'copy', 'extend', '__iadd__', 'pop', 'append', '__imul__', 'clear', 'remove', '__alloc__', '__setitem__'}
>>> L1 = [1, 5, 2, 4, 3]
>>> L2 = [5, 1 ,2, 3, 4]
>>> L1 == L2
False
>>> set(L1) == set(L2)
True
布爾型
Python通過判斷表達式的真假得到布爾值。
布爾型實際上是特殊的整數類型,True
對應1,False
對應0。
>>> True + 4
5
>>> False + 4
4
數值擴展
NumPy可以處理更複雜的數值類型,如向量、矩陣。