數(shù)字、日期和時間的高級處理
嗯,學(xué)習(xí)發(fā)現(xiàn)有些方法函數(shù)即使大版本相同,小版本也是有些差距的,這是我學(xué)習(xí)的版本
┌──[root@liruilongs.github.io]-[~]
└─$Python/ target=_blank class=infotextkey>Python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
浮點數(shù)執(zhí)行指定精度的舍入運算。
「對浮點數(shù)執(zhí)行指定精度的舍入運算。」
對于簡單的舍入運算,使用內(nèi)置的round(value, ndigits)函數(shù)即可。
┌──[root@liruilongs.github.io]-[~]
└─$python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> round(1.23, 1)
1.2
>>> round(1.25361,3)
1.254
>>>
當(dāng)一個值剛好在兩個邊界的中間的時候, round 函數(shù)返回離它最近的偶數(shù)。對 1.5 或者 2.5 的舍入運算都會得到 2。
>>> round(1.5,0)
2.0
>>> round(2.5,0)
2.0
>>>
傳給 round() 函數(shù)的 ndigits 參數(shù)可以是負(fù)數(shù),舍入運算會作用在十位、百位、千位等上面
>>> a = 1627731
>>> round(a, -1)
1627730
>>> round(a, -2)
1627700
>>>
不要將舍入和格式化輸出搞混淆了。如果你的目的只是簡單的輸出一定寬度的數(shù),你不需要使用round()函數(shù)。而僅僅只需要在格式化的時候指定精度即可,
>>> x = 1.23456
>>> format(x, '0.2f')
'1.23'
>>> format(x, '0.4f')
'1.2346'
>>> 'value is {:0.3f}'.format(x)
'value is 1.235'
>>>
可以看到,輸出的數(shù)據(jù)并不相同,一個是字符串,一個是int等數(shù)字類型
>>> type(format(1.5, '0.0f'))
<class 'str'>
>>> type(round(a, -1))
<class 'int'>
>>> type(round(1.25361,3))
<class 'float'>
>>>
不要試著去舍入浮點值來” 修正” 表面上看起來正確的問題。大多數(shù)使用到浮點的程序,沒有必要也不推薦這樣做.
>>> 2.1 + 4.2
6.300000000000001
>>> round(2.1 + 4.2,2)
6.3
>>>
執(zhí)行精確的浮點數(shù)運算
「要對浮點數(shù)執(zhí)行精確的計算操作,并且不希望有任何小誤差的出現(xiàn)。」
浮點數(shù)的一個普遍問題是它們并不能精確的表示十進制數(shù)。并且,即使是最簡單的數(shù)學(xué)運算也會產(chǎn)生小的誤差
就那上面的Demo來講
>>> 2.1 + 4.2
6.300000000000001
>>> round(2.1 + 4.2,2)
6.3
>>> (2.1 + 4.2) == 6.3
False
>>>
錯誤是由底層 CPU 和 IEEE 754 標(biāo)準(zhǔn)通過自己的浮點單位去執(zhí)行算術(shù)時的特征。由于 Python 的浮點數(shù)據(jù)類型使用底層表示存儲數(shù)據(jù),因此你沒辦法去避免這樣的誤差。在JAVA里也出現(xiàn)同樣的問題
public static void main(String[] args) {
System.out.println(2.1+4.2);
}
// 6.300000000000001
如果你想更加精確 (并能容忍一定的性能損耗),你可以使用 decimal 模塊
>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True
>>>
decimal 模塊的一個主要特征是允許你控制計算的每一方面,包括數(shù)字位數(shù)和四舍五入運算。為了這樣做,你先得創(chuàng)建一個本地上下文并更改它的設(shè)置
>>> from decimal import localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a / b)
0.7647058823529411764705882353
>>> with localcontext() as ctx:
... ctx.prec = 3
... print(a / b)
...
0.765
>>> with localcontext() as ctx:
... ctx.prec = 50
... print(a / b)
...
0.76470588235294117647058823529411764705882352941176
>>>
decimal 模塊實現(xiàn)了 IBM 的” 通用小數(shù)運算規(guī)范”,Python 新手會傾向于使用 decimal 模塊來處理浮點數(shù)的精確運算。但是要根據(jù)業(yè)務(wù)需求來處理,decimal 模塊主要用在涉及到金融的領(lǐng)域。
- 原生的浮點數(shù)計算要快的多
- 在真實世界中很少會要求精確到普通浮點數(shù)能提供的 17 位精度
其他的一些誤差,大數(shù)和小數(shù)的加法運算(在Java里也出現(xiàn)同樣的問題)
>>> nums = [1.23e+18, 1, -1.23e+18]
>>> sum(nums)
0.0
>>> 1.23e+18 + 1 + -1.23e+18
0.0
>>> 1.23e+18 + -1.23e+18 + 1
1.0
>>>
public static void main(String[] args) {
System.out.println(1.23e+18 + 1 + -1.23e+18);
} // 0.0
py的解決辦法
>>> import math
>>> math.fsum(nums)
1.0
>>>
數(shù)字的格式化輸出
「你需要將數(shù)字格式化后輸出,并控制數(shù)字的位數(shù)、對齊、千位分隔符和其他的細(xì)節(jié)。」
格式化輸出單個數(shù)字的時候,可以使用內(nèi)置的format()函數(shù),這個前面也有好的Demo,不多說
>>> x = 1234.56789
>>> format(x, '0.2f')
'1234.57'
>>> format(x, '>10.1f')
' 1234.6'
>>> format(x, '<10.1f')
'1234.6 '
>>> format(x, '0>10.1f')
'00001234.6'
>>> format(x, '^10.1f')
' 1234.6 '
>>> format(x, ',')
'1,234.56789'
>>> format(x, '0,.1f')
'1,234.6'
>>>
使用指數(shù)記法,將 f 改成 e 或者 E(取決于指數(shù)輸出的大小寫形式)。
>>> format(x, 'e')
'1.234568e+03'
>>> format(x, '0.2E')
'1.23E+03'
>>>
同時指定寬度和精度的一般形式是 '[<>ˆ]?width[,]?(.digits)?' ,其中 width 和 digits 為整數(shù),?代表可選部分。同樣的格式也被用在字符串的format()方法中。
>>> 'The value is {:0,.2f}'.format(x)
'The value is 1,234.57'
>>>
format() 函數(shù) 同時適用于浮點數(shù)和 decimal模塊中的 Decimal 數(shù)字對象。
>>> x
1234.56789
>>> format(x, '0.1f')
'1234.6'
>>> format(-x, '0.1f')
'-1234.6'
>>>
包含千位符的格式化跟本地化沒有關(guān)系。如果你需要根據(jù)地區(qū)來顯示千位符,你需要自己去調(diào)查下 locale 模塊中的函數(shù)了。你同樣也
可以使用字符串的 translate()方法來交換千位符。
>>> swap_separators = { ord('.'):',', ord(','):'.' }
>>> format(x, ',').translate(swap_separators)
'1.234,56789'
>>> x
1234.56789
>>>
也可以直接使用% 來格式化數(shù)字的
>>> x
1234.56789
>>> '%0.2f' % x
'1234.57'
>>> '%10.1f' % x
' 1234.6'
>>> '%-10.1f' % x
'1234.6 '
二進制、八進制和十六進制整數(shù)轉(zhuǎn)化輸出
「轉(zhuǎn)換或者輸出使用二進制,八進制或十六進制表示的整數(shù)。」
為了將整數(shù)轉(zhuǎn)換為二進制、八進制或十六進制的文本串,可以分別使用bin() ,oct() 或 hex()函數(shù):
>>> x = 1234
>>> bin(x)
'0b10011010010'
>>> oct(x)
'0o2322'
>>> hex(x)
'0x4d2'
>>>
不想輸出 0b , 0o 或者 0x 的前綴的話,可以使用format()函數(shù)
>>> format(x, 'b')
'10011010010'
>>> format(x, 'o')
'2322'
>>> format(x, 'x')
'4d2'
>>>
整數(shù)是有符號的,所以如果你在處理負(fù)數(shù)的話,輸出結(jié)果會包含一個負(fù)號
>>> x = -1234
>>> format(x, 'b')
'-10011010010'
>>> format(x, 'x')
'-4d2'
>>>
如果你想產(chǎn)生一個無符號值,你需要增加一個指示最大位長度的值。比如為了顯示32 位的值,
>>> x = -1234
>>> format(2**32 + x, 'b')
'11111111111111111111101100101110'
>>> format(2**32 + x, 'x')
'fffffb2e'
>>>
為了以不同的進制轉(zhuǎn)換整數(shù)字符串,簡單的使用帶有進制的int()函數(shù)即可:
>>> int('4d2', 16)
1234
>>> int('10011010010', 2)
1234
>>>
Python 指定八進制數(shù)的語法跟其他語言稍有不同
>>> import os
>>> os.chmod('script.py', 0755)
File "<stdin>", line 1
os.chmod('script.py', 0755)
^
SyntaxError: invalid token
>>> os.chmod('App.py', 0o755)
>>
字節(jié)字符串到大整數(shù)的相互轉(zhuǎn)化
「你有一個字節(jié)字符串并想將它解壓成一個整數(shù)。或者,你需要將一個大整數(shù)轉(zhuǎn)換為一個字節(jié)字符串。」
大整數(shù)和字節(jié)字符串之間的轉(zhuǎn)換操作并不常見,一些場景也會用到,IPv6 網(wǎng)絡(luò)地址使用一個 128 位的整數(shù)表示。如果你要從一個數(shù)據(jù)記錄中提取這樣的值的時候
處理一個擁有 128 位長的 16 個元素的字節(jié)字符串
>>> data = b'x00x124Vx00xx90xabx00xcdxefx01x00#x004'
>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>>
為了將一個大整數(shù)轉(zhuǎn)換為一個字節(jié)字符串,使用int.to_bytes()方法,并像下面這樣指定字節(jié)數(shù)和字節(jié)順序:
>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'big')
b'x00x124Vx00xx90xabx00xcdxefx01x00#x004'
>>> x.to_bytes(16, 'little')
b'4x00#x00x01xefxcdx00xabx90xx00V4x12x00'
>>>
這里字節(jié)順序規(guī)則 (little 或 big) 僅僅指定了構(gòu)建整數(shù)時的字節(jié)的低位高位排列方式。
>>> x = 0x01020304
>>> x.to_bytes(4, 'big')
b'x01x02x03x04'
>>> x.to_bytes(4, 'little')
b'x04x03x02x01'
>>>
如果你試著將一個整數(shù)打包為字節(jié)字符串,那么它就不合適了,你會得到一個錯誤。如果需要的話,你可以使用 int.bit_length() 方法來決定需要多少字節(jié)位來存儲這個值。(** 表示乘方,divmod() 函數(shù)把除數(shù)和余數(shù)運算結(jié)果結(jié)合起來,返回一個包含商和余數(shù)的元組(a // b, a % b)。)
>>> x = 523 ** 23
>>> x
335381300113661875107536852714019056160355655333978849017944067
>>> x.to_bytes(16, 'little')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> x.bit_length()
208
>>> nbytes, rem = divmod(x.bit_length(), 8)
>>> if rem:
... nbytes += 1
...
>>>
>>> x.to_bytes(nbytes, 'little')
b'x03Xxf1x82iTx96xacxc7cx16xf3xb9xcfx18xeexecx91xd1x98xa2xc8xd9Rxb5xd0'
>>>
復(fù)數(shù)的數(shù)學(xué)運算
「使用復(fù)數(shù)來執(zhí)行一些計算操作。」
復(fù)數(shù)可以用使用函數(shù)complex(real, imag)或者是帶有后綴j的浮點數(shù)來指定。
>>> a = complex(2, 4)
>>> b = 3 - 5j
>>> a
(2+4j)
>>> b
(3-5j)
>>>
對應(yīng)的實部、虛部和共軛復(fù)數(shù)可以很容易的獲取。
>>> a.real
2.0
>>> a.imag
4.0
>>> a.conjugate()
(2-4j)
>>>
常見的數(shù)學(xué)四則運算
>>> a + b
(5-1j)
>>> a * b
(26+2j)
>>> a / b
(-0.4117647058823529+0.6470588235294118j)
>>> abs(a)
4.47213595499958
>>>
如果要執(zhí)行其他的復(fù)數(shù)函數(shù)比如正弦、余弦或平方根,使用 cmath 模塊:
>>> import cmath
>>> cmath.sin(a)
(24.83130584894638-11.356612711218174j)
>>> cmath.cos(a)
(-11.36423470640106-24.814651485634187j)
>>> cmath.exp(a)
(-4.829809383269385-5.5920560936409816j)
>>>
Python 中大部分與數(shù)學(xué)相關(guān)的模塊都能處理復(fù)數(shù)。使用 numpy 很容易的構(gòu)造一個復(fù)數(shù)數(shù)組并在這個數(shù)組上執(zhí)行各種操作
>>> import numpy as np
>>> a = np.array([2+3j, 4+5j, 6-7j, 8+9j])
>>> a
array([ 2.+3.j, 4.+5.j, 6.-7.j, 8.+9.j])
>>> a + 2
array([ 4.+3.j, 6.+5.j, 8.-7.j, 10.+9.j])
>>> np.sin(a)
array([ 9.15449915 -4.16890696j, -56.16227422 -48.50245524j,
-153.20827755-526.47684926j, 4008.42651446-589.49948373j])
>>>
Python 的標(biāo)準(zhǔn)數(shù)學(xué)函數(shù)確實情況下并不能產(chǎn)生復(fù)數(shù)值,因此你的代碼中不可能會出現(xiàn)復(fù)數(shù)返回值
>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>
如果你想生成一個復(fù)數(shù)返回結(jié)果,你必須顯示的使用 cmath 模塊
>>> import cmath
>>> cmath.sqrt(-1)
1j
>>>
處理無窮大和NaN
「你想創(chuàng)建或測試正無窮、負(fù)無窮或 NaN(非數(shù)字) 的浮點數(shù)。」
Python 并沒有特殊的語法來表示這些特殊的浮點值,但是可以使用float()來創(chuàng)建它們。比如:
>>> a = float('inf')
>>> b = float('-inf')
>>> c = float('nan')
>>> a
inf
>>> b
-inf
>>> c
nan
>>>
使用 math.isinf() 和 math.isnan() 函數(shù)來測試
>>> math.isinf(a)
True
>>> math.isnan(c)
True
>>>
無窮大數(shù)在執(zhí)行數(shù)學(xué)計算的時候會傳播
>>> a = float('inf')
>>> a + 45
inf
>>> a * 10
inf
>>> 10 / a
0.0
>>>
有些操作時未定義的并會返回一個 NaN 結(jié)果
>>> a = float('inf')
>>> a/a
nan
>>> b = float('-inf')
>>> a + b
nan
>>>
NaN 值會在所有操作中傳播,而不會產(chǎn)生異常
>>> c = float('nan')
>>> c + 23
nan
>>> c / 2
nan
>>> c * 2
nan
>>> math.sqrt(c)
nan
>>>
NaN 之間的比較操作總是返回 False
>>> c = float('nan')
>>> d = float('nan')
>>> c == d
False
>>> c is d
False
>>>
有時候程序員想改變 Python 默認(rèn)行為,在返回?zé)o窮大或 NaN 結(jié)果的操作中拋出異常。 fpectl 模塊可以用來改變這種行為,但是它在標(biāo)準(zhǔn)的 Python 構(gòu)建中并沒有被啟用,它是平臺相關(guān)的,并且針對的是專家級程序員。
分?jǐn)?shù)運算
「在一個允許接受分?jǐn)?shù)形式的測試單位并以分?jǐn)?shù)形式執(zhí)行運算的程序中,直接使用分?jǐn)?shù)可以減少手動轉(zhuǎn)換為小數(shù)或浮點數(shù)的工作」
fractions 模塊可以被用來執(zhí)行包含分?jǐn)?shù)的數(shù)學(xué)運算。
>>> from fractions import Fraction
>>> a = Fraction(5, 4)
>>> b = Fraction(7, 16)
>>> print(a + b)
27/16
>>> print(a * b)
35/64
>>> c = a * b
>>> c.numerator
35
>>> c.denominator
64
>>> float(c)
0.546875
>>>
limit_denominator(max_denominator) 方法可以找到并返回一個 Fraction 使得其值最接近 self 并且分母不大于 max_denominator。
as_integer_ratio()用于將浮點數(shù)轉(zhuǎn)化為分?jǐn)?shù)
>>> print(c.limit_denominator(8))
4/7
>>> x = 3.75
>>> y = Fraction(*x.as_integer_ratio())
>>> y
Fraction(15, 4)
>>>
處理大型數(shù)組的計算
「在大數(shù)據(jù)集 (比如數(shù)組或網(wǎng)格) 上面執(zhí)行計算。」
到數(shù)組的重量級運算操作,可以使用 NumPy 庫。 NumPy 的一個主要特征是它會給 Python 提供一個數(shù)組對象,相比標(biāo)準(zhǔn)的 Python 列表而已更適合用來做數(shù)學(xué)運算。
額,數(shù)學(xué)忘光了....這里先記錄下,list運算并沒有遵守矩陣運算規(guī)則,肯定不能直接用
>>> # Python lists
>>> x = [1, 2, 3, 4]
>>> y = [5, 6, 7, 8]
>>> x * 2
[1, 2, 3, 4, 1, 2, 3, 4]
>>> x + 10
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list
>>> x + y
[1, 2, 3, 4, 5, 6, 7, 8]
>>> # Numpy arrays
>>> import numpy as np
>>> ax = np.array([1, 2, 3, 4])
>>> ay = np.array([5, 6, 7, 8])
>>> ax * 2
NumPy 還為數(shù)組操作提供了大量的通用函數(shù),這些函數(shù)可以作為 math 模塊中類似函數(shù)的替代
>>> np.sqrt(ax)
array([ 1. , 1.41421356, 1.73205081, 2. ])
>>> np.cos(ax)
array([ 0.54030231, -0.41614684, -0.9899925 , -0.65364362])
>>>
底層實現(xiàn)中, NumPy 數(shù)組使用了 C 或者 Fortran 語言的機制分配內(nèi)存。也就是說,它們是一個非常大的連續(xù)的并由同類型數(shù)據(jù)組成的內(nèi)存區(qū)域。所以,你可以構(gòu)造一個比普通 Python 列表大的多的數(shù)組。比如,如果你想構(gòu)造一個 10,000*10,000 的浮點數(shù)二維網(wǎng)格,很輕松
>>> import numpy as np
>>> grid = np.zeros(shape=(10000,10000), dtype=float)
>>> grid
array([[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
...,
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.],
[0., 0., 0., ..., 0., 0., 0.]])
>>>
所有的普通操作是遵循矩陣運算法則的:
>>> grid += 10
>>> grid
array([[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
...,
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.],
[10., 10., 10., ..., 10., 10., 10.]])
關(guān)于 NumPy,它擴展 Python 列表的索引功能 - 特別是對于多維數(shù)組,這里和matlab的數(shù)組語法有些類似
>>> a = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
>>> a
array([[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9, 10, 11, 12]])
>>> # Select row 1
>>> a[1]
array([5, 6, 7, 8])
>>> # Select column 1
>>> a[:,1]
array([ 2, 6, 10])
>>> # 選擇子區(qū)域并進行修改
>>> a[1:3, 1:3]
array([[ 6, 7],
[10, 11]])
>>> a[1:3, 1:3] += 10
>>> a
array([[ 1, 2, 3, 4],
[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])
>>> # 在所有行的操作上廣播行向量
>>> a + [100, 101, 102, 103]
array([[101, 103, 105, 107],
[105, 117, 119, 111],
[109, 121, 123, 115]])
>>> a
array([[ 1, 2, 3, 4],
[ 5, 16, 17, 8],
[ 9, 20, 21, 12]])
>>> # 數(shù)組的條件賦值
>>> np.where(a < 10, a, 10)
array([[ 1, 2, 3, 4],
[ 5, 10, 10, 8],
[ 9, 10, 10, 10]])
>>>
矩陣和線性代數(shù)的計算
「執(zhí)行矩陣和線性代數(shù)運算,比如矩陣乘法、尋找行列式、求解線性方程組等。」
這些一般做數(shù)據(jù)分析會用到,但是數(shù)據(jù)分析需要有一定的數(shù)學(xué)基礎(chǔ),時間關(guān)系這里簡單了解下,好后悔之前沒好好學(xué),都忘光了...
>>> import numpy as np
>>> m = np.matrix([[1,-2,3],[0,4,5],[7,8,-9]])
>>> m
matrix([[ 1, -2, 3],
[ 0, 4, 5],
[ 7, 8, -9]])
>>> # Return transpose
>>> m.T
matrix([[ 1, 0, 7],
[-2, 4, 8],
[ 3, 5, -9]])
>>> # Return inverse
>>> m.I
matrix([[ 0.33043478, -0.02608696, 0.09565217],
[-0.15217391, 0.13043478, 0.02173913],
[ 0.12173913, 0.09565217, -0.0173913 ]])
>>> # Create a vector and multiply
>>> v = np.matrix([[2],[3],[4]])
>>> v
matrix([[2],
[3],
[4]])
>>> m * v
matrix([[ 8],
[32],
[ 2]])
>>>
在numpy.linalg子包中找到更多的操作函數(shù)
>>> import numpy.linalg
>>> # Determinant
>>> numpy.linalg.det(m)
-229.99999999999983
>>> # Eigenvalues
>>> numpy.linalg.eigvals(m)
array([-13.11474312, 2.75956154, 6.35518158])
>>> # Solve for x in mx = v
>>> x = numpy.linalg.solve(m, v)
>>> x
matrix([[ 0.96521739],
[ 0.17391304],
[ 0.46086957]])
>>> m * x
matrix([[ 2.],
[ 3.],
[ 4.]])
>>> v
matrix([[2],
[3],
[4]])
>>>
隨機選擇
「從一個序列中隨機抽取若干元素,或者想生成幾個隨機數(shù)。」
random 模塊有大量的函數(shù)用來產(chǎn)生隨機數(shù)和隨機選擇元素。比如,要想從一個序列中隨機的抽取一個元素,可以使用 random.choice() :
>>> import random
>>> values = [1, 2, 3, 4, 5, 6]
>>> random.choice(values)
2
>>> random.choice(values)
3
>>> random.choice(values)
1
>>> random.choice(values)
4
>>> random.choice(values)
6
>>>
為了提取出 N 個不同元素的樣本用來做進一步的操作,可以使用 random.sample()
>>> random.sample(values, 2)
[6, 2]
>>> random.sample(values, 2)
[4, 3]
>>> random.sample(values, 3)
[4, 3, 1]
>>> random.sample(values, 3)
[5, 4, 1]
>>>
只是想打亂序列中元素的順序,可以使用random.shuffle():
>>> random.shuffle(values)
>>> values
[2, 4, 6, 5, 3, 1]
>>> random.shuffle(values)
>>> values
[3, 5, 2, 1, 6, 4]
>>>
生成隨機整數(shù),請使用 random.randint() :
>>> random.randint(0,10)
2
>>> random.randint(0,1000)
452
為了生成 0 到 1 范圍內(nèi)均勻分布的浮點數(shù),使用random.random()
>>> random.random()
0.9406677561675867
>>> random.random()
0.133129581343897
>>> random.random()
0.4144991136919316
>>>
要獲取 N 位隨機位 (二進制) 的整數(shù),使用 random.getrandbits() :
>>> random.getrandbits(200)
335837000776573622800628485064121869519521710558559406913275
>>>
random 模塊使用 Mersenne Twister 算法來計算生成隨機數(shù)。這是一個確定性算法,但是你可以通過random.seed()函數(shù)修改初始化種子
random.seed() # Seed based on system time or os.urandom()
random.seed(12345) # Seed based on integer given
random.seed(b'bytedata') # Seed based on byte data
除了上述介紹的功能,random 模塊還包含基于均勻分布、高斯分布和其他分布的隨機數(shù)生成函數(shù)。比如,random.uniform()計算均勻分布隨機數(shù), random.gauss()計算正態(tài)分布隨機數(shù)。
。。。。概率論的知識,唉,沒好好聽課
在 random 模塊中的函數(shù)不應(yīng)該用在和密碼學(xué)相關(guān)的程序中。,可以使用 ssl 模塊中相應(yīng)的函數(shù)。比如, ssl.RAND_bytes() 可以用來生成一個安全的隨機字節(jié)序列。
基本的日期與時間轉(zhuǎn)換
「你需要執(zhí)行簡單的時間轉(zhuǎn)換,比如天到秒,小時到分鐘等的轉(zhuǎn)換。」
為了執(zhí)行不同時間單位的轉(zhuǎn)換和計算,請使用 datetime 模塊。比如,為了表示一個時間段,可以創(chuàng)建一個 timedelta 實例,可以直接轉(zhuǎn)化天到秒,但是小時的話需要運算
>>> a = timedelta(days=2, hours=6)
>>> b = timedelta(hours=4.5)
>>> c = a + b
>>> c
datetime.timedelta(2, 37800)
>>> c.days
2
>>> c.hours
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'datetime.timedelta' object has no attribute 'hours'
>>> c.seconds
File "<stdin>", line 1
c.seconds
^
IndentationError: unexpected indent
>>> c.seconds
37800
>>> c.seconds / 3600
10.5
>>> c.total_seconds() / 3600
58.5
>>>
表示指定的日期和時間,先創(chuàng)建一個datetime實例然后使用標(biāo)準(zhǔn)的數(shù)學(xué)運算來操作它們
>>> from datetime import datetime, date, timedelta
>>> from datetime import datetime
>>> a = datetime(2022,5,4)
>>> a
datetime.datetime(2022, 5, 4, 0, 0)
>>> a + timedelta(days=20)
datetime.datetime(2022, 5, 24, 0, 0)
>>> print(a + timedelta(days=20))
2022-05-24 00:00:00
>>> a - datetime(2021,1,1)
datetime.timedelta(488)
>>> (a - datetime(2021,1,1)).days
488
>>> datetime.today()
datetime.datetime(2022, 5, 4, 12, 21, 29, 867955)
>>> print(datetime.today())
2022-05-04 12:21:40.399207
>>> print(datetime.today() + timedelta(minutes=10))
2022-05-04 12:32:05.150013
>>>
需要注意的是 datetime 會自動處理閏年,基本的日期和時間處理問題, datetime 模塊以及足夠了,需要更加復(fù)雜的日期操作,可以考慮使用 dateutil 模塊,許多類似的時間計算可以使用 dateutil.relativedelta() 函數(shù)代替
┌──[root@liruilongs.github.io]-[~]
└─$ python3 -m pip install python-dateutil
>>> a = datetime(2012, 9, 23)
>>> a + timedelta(months=1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'months' is an invalid keyword argument for this function
>>>
>>> from dateutil.relativedelta import relativedelta
>>> a + relativedelta(months=+1)
datetime.datetime(2012, 10, 23, 0, 0)
>>> a + relativedelta(months=+4)
datetime.datetime(2013, 1, 23, 0, 0)
>>>
>>> # Time between two dates
>>> b = datetime(2012, 12, 21)
>>> d = b - a
>>> d
datetime.timedelta(89)
>>> d = relativedelta(b, a)
>>> d
relativedelta(months=+2, days=+28)
>>> d.months
2
>>> d.days
28
>>>
計算上周5的日期
「你需要查找星期中某一天最后出現(xiàn)的日期,比如星期五。」
Python 的 datetime 模塊中有工具函數(shù)和類可以幫助你執(zhí)行這樣的計算
先將開始日期和目標(biāo)日期映射到星期數(shù)組的位置上 (星期一索引為 0),然后通過模運算計算出目標(biāo)日期要經(jīng)過多少天才能到達開始日期。然后用開始日期減去那個時間差即得到結(jié)果日期。
from datetime import datetime, timedelta
weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
def get_previous_byday(dayname, start_date=None):
if start_date is None:
start_date = datetime.today()
day_num = start_date.weekday()
day_num_target = weekdays.index(dayname)
days_ago = (7 + day_num - day_num_target) % 7 #這里不是特別懂
if days_ago == 0:
days_ago = 7
target_date = start_date - timedelta(days=days_ago)
return target_date
可選的 start_date 參數(shù)可以由另外一個 datetime 實例來提供
datetime.today()
print(get_previous_byday('Monday'))
get_previous_byday('Sunday', datetime(2022, 12, 21))
找出當(dāng)月的日期范圍
「需要在當(dāng)前月份中循環(huán)每一天,想找到一個計算這個日期范圍的高效方法。」
需要事先構(gòu)造一個包含所有日期的列表。你可以先計算出開始日期和結(jié)束日期,然后在你步進的時候使用 datetime.timedelta 對象遞增這個日期變量即可。
from datetime import datetime, date, timedelta
import calendar
def get_month_range(start_date=None):
if start_date is None:
start_date = date.today().replace(day=1)
_, days_in_month = calendar.monthrange(start_date.year, start_date.month)
end_date = start_date + timedelta(days=days_in_month)
return (start_date, end_date)
a_day = timedelta(days=1)
first_day, last_day = get_month_range()
while first_day < last_day:
print(first_day)
first_day += a_day
對應(yīng)月份第一天的日期,使用 date 或 datetime 對象的 replace() 方法簡單的將days屬性設(shè)置成1即可。replace() 方法一個好處就是它會創(chuàng)建和你開始傳入對象類型相同的對象
使用 calendar.monthrange() 函數(shù)來找出該月的總天數(shù)
將字符串轉(zhuǎn)換為日期
「應(yīng)用程序接受字符串格式的輸入,但是你想將它們轉(zhuǎn)換為 datetime 對象以便在上面執(zhí)行非字符串操作」。
>>> from datetime import datetime
>>> text = '2012-09-20'
>>> y = datetime.strptime(text, '%Y-%m-%d')
>>> z = datetime.now()
>>> print( datetime.now() - datetime.strptime(text, '%Y-%m-%d'))
19:37:02.113440
>>>
datetime.strptime() 方法支持很多的格式化代碼,比如 %Y 代表 4 位數(shù)年份, %m 代表兩位數(shù)月份,strptime() 的性能要比你想象中的差很多,如果你已經(jīng)知道所以日期格式是YYYY-MM-DD,你可以像下面這樣 實現(xiàn)一個解析函數(shù).
from datetime import datetime
def parse_ymd(s):
year_s, mon_s, day_s = s.split('-')
return datetime(int(year_s), int(mon_s), int(day_s))
處理涉及到時區(qū)的日期問題
「你有一個安排在 2012 年 12 月 21 日早上 9:30 的電話會議,地點在芝加哥。而你的朋友在印度的班加羅爾,那么他應(yīng)該在當(dāng)?shù)貢r間幾點參加這個會議呢?」
幾乎所有涉及到時區(qū)的問題,你都應(yīng)該使用 pytz 模塊。這個包提供了 Olson 時區(qū)數(shù)據(jù)庫,它是時區(qū)信息的事實上的標(biāo)準(zhǔn),在很多語言和操作系統(tǒng)里面都可以找到。
pytz 模塊一個主要用途是將 datetime 庫創(chuàng)建的簡單日期對象本地化
>>> from datetime import datetime
>>> from pytz import timezone
>>> datetime.today()
datetime.datetime(2022, 5, 4, 18, 58, 56, 843102)
>>> print(datetime.today())
2022-05-04 18:59:12.433695
>>> print(central.localize(datetime.today()).astimezone(timezone('US/Central')))
2022-05-04 06:04:33.518289-05:00
本地化日期上執(zhí)行計算,你需要特別注意夏令時轉(zhuǎn)換和其他細(xì)節(jié)。這個我們不涉及,先不看
處理本地化日期的通常的策略先將所有日期轉(zhuǎn)換為 UTC 時間
>>> import pytz
>>> utc_d = datetime.today().astimezone(pytz.utc)
>>> utc_d
datetime.datetime(2022, 5, 4, 11, 11, 35, 701740, tzinfo=<UTC>)
>>> print(utc_d)
2022-05-04 11:11:35.701740+00:00
>>> from datetime import datetime, date, timedelta
>>> later_utc = utc_d + timedelta(hours=8)
>>> print(later_utc)
2022-05-04 19:11:35.701740+00:00
from datetime import datetime
from datetime import timedelta
from datetime import timezone
SHA_TZ = timezone(
timedelta(hours=8),
name='Asia/Shanghai',
)
# 協(xié)調(diào)世界時
utc_now = datetime.utcnow().replace(tzinfo=timezone.utc)
print("UTC:")
print(utc_now, utc_now.time())
print(utc_now.date(), utc_now.tzname())
# 北京時間
beijing_now = utc_now.astimezone(SHA_TZ)
print("Beijing:")
print(beijing_now.strftime('%H_%M_%S'))
print(beijing_now, beijing_now.time())
print(beijing_now.date(), beijing_now.tzname())
# 系統(tǒng)默認(rèn)時區(qū)
local_now = utc_now.astimezone()
print("Default:")
print(local_now, local_now.time())
print(local_now.date(), local_now.tzname())