Python學習進階教程(11)—數值類型

(本號正在連續推出以Python官網文檔為主線的完整的系統的學習Python的系列文章和視頻,感興趣的朋友們歡迎搜索關注。本文及後續文章如無特別聲明均以Windows平臺作為演示平臺,Python版本為:3.8.1)


注意:開始學習“Python學習進階教程”系列內容前須已經學習過“Python學習入門教程”系列內容】

數值類型的常用的通用操作已在"Python學習入門(4)—數值運算"中詳細介紹過了,本文作為進階教程,將主要介紹各種數值類型的特定操作。

整數類型的位操作

位操作只對整數有意義。按位運算的結果就像在具有無限個符號位的2的補碼中進行運算的結果一樣。

二元按位運算的優先級均低於數值運算,高於比較運算;一元運算~具有與其他一元數字運算(+和-)相同的優先級。

下表列出了按優先級升序排列的按位操作:

Python學習進階教程(11)—數值類型

位運算符

注:

  • 使用負的移位計數是非法的,會引起ValueError錯誤。
  • 一個n位的左移相當於一個沒有溢出檢查的乘以pow(2,n)運算。
  • 一個n位的右移相當於一個沒有溢出檢查的除以pow(2,n)運算。
  • 使用具有一個額外符號擴展位的有限的2的補碼(工作位寬度為1 + max(x.bit_length(), y.bit_length())進行這些運算時,完全可得到與具有無限個符號位時同樣的結果

整型的附加方法

int類型實現了numbers.Integral抽象類。除此之外,也提供了下面這些方法:

int.bit_length() 返回二進制中表示整數所需的位數,不包括符號和前導零:

<code>>>> n = -37
>>> bin(n)
'-0b100101'
>>> n.bit_length()
6/<code>

更精確地說,如果x是非0整數,則x.bit_length()是一個唯一的正整數k,使得2**(k-1) <= abs(x) < 2**k。同樣地,當abs(x)大小合適對其求對數可以得到一個正確四捨五入的對數時,那麼k = 1 + int(log(abs(x),2))。如果x為0,則x.bit_length()返回0。

該方法等價於:

<code>def bit_length(self):
s = bin(self) # binary representation: bin(-37) --> '-0b100101'
s = s.lstrip('-0b') # remove leading zeros and minus sign
return len(s) # len('100101') --> 6
int.to_bytes(length, byteorder, *, signed=False) 返回一個表示該整數的字節數組
>>>(1024).to_bytes(2, byteorder='big')
b'\\\\\\x04\\\\\\x00'
>>> (1024).to_bytes(10, byteorder='big')
b'\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x00\\\\\\x04\\\\\\x00'
>>> (-1024).to_bytes(10, byteorder='big', signed=True)
b'\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xff\\\\\\xfc\\\\\\x00'
>>> x = 1000
>>> x.to_bytes((x.bit_length() + 7) // 8, byteorder='little')
b'\\\\\\xe8\\\\\\x03'/<code>

該整數用length個字節表示。如果整數不能用給定的字節數表示,則會引發OverflowError。

byteorder參數確定用於表示整數的字節順序。如果byteorder是“big”,則最有效的字節位於字節數組的開頭。如果byteorder是“little”,則最有效的字節位於字節數組的末尾。要請求主機系統的本地字節順序,請使用sys.byteorder作為字節順序值。

signed參數確定是否使用2的補數來表示整數。如果signed是False,並且給定了一個負整數,則會引發OverflowError。signed的默認值為False。

classmethod int.from_bytes(bytes, byteorder, *, signed=False) 返回一個由給定字節數組表示的整數。

<code>def bit_length(self):
s = bin(self) # binary representation: bin(-37) --> '-0b100101'

s = s.lstrip('-0b') # remove leading zeros and minus sign
return len(s) # len('100101') --> 6/<code>

int.as_integer_ratio() 返回一對整數,它們的比完全等於原始整數,並且分母為正。整數(整數)的比率總是整數作為分子,1作為分母。

浮點數的附加方法

浮點類型實現了 numbers.Real抽象類,也提供了下面這些方法:

float.as_integer_ratio() 返回一對整數,它們的比完全等於原始浮點數,並且分母為正。在無窮大上將引發OverflowError,在NaNs上將引發 ValueError。

float.is_integer() 如果float實例是有限整數值,則返回True,否則返回False:

<code>>>> (-2.0).is_integer()
True
>>> (3.2).is_integer()
False/<code>

由於Python的浮點數是作為二進制數存儲在內部的,因此浮點數與十進制字符串之間的通常會引入一個小的舍入錯誤。相反,十六進制字符串能夠精確表示和指定浮點數,這在調試和數值工作上非常有用。有兩種方法支持對十六進制字符串的轉換:

float.hex() 以十六進制字符串的形式返回浮點數的表示形式。對於有限的浮點數,這種表示法總是包含一個前導0x和一個後導p和指數。

classmethod float.fromhex(s) 類方法來返回由十六進制字符串s表示的浮點數。字符串s可以有開頭和結尾的空白。

注意: float.hex()是一個實例方法,而float.fromhex()是一個類方法。

方法中的十六進制字符串採用如下形式:

<code>[sign] ['0x'] integer ['.' fraction] ['p' exponent]/<code>

其中可選的sign可以是+或-,integer和fraction是十六進制數字串,exponent是帶可選前導符號的十進制整數。字符串中字符不區分大小寫,且integer或fraction中必須至少有一個十六進制數字。此語法類似於C99標準第6.4.4.2節中指定的語法,也類似於Java 1.5及以後版本中使用的語法。因此,float.hex()的輸出可以作為C或Java代碼中的十六進制浮點字面值,C的%a或Java的Doubleto.HexString生成的十六進制字符串也可以被float.fromhex()使用。

注意:exponent是用十進制而不是十六進制表示的,它表示2的exponent次冪,該結果乘以前面的係數就是整個浮點數的值。例如,十六進制字符串0x3。a7p10表示浮點數(3 + 10./16 + 7./16**2) * 2.0**10,或3740.0:

<code>>>> float.fromhex('0x3.a7p10')
3740.0/<code>

對3740.0進行反向轉換會得到一個不同的十六進制字符串,表示相同的數值:

<code>>>> float.hex(3740.0)
'0x1.d380000000000p+11'/<code>

數值類型的哈希

對於可能是不同類型的數字x和y,每當x == y時,都要求hash(x) == hash(y)(有關更多詳細信息,請參閱"Python學習進階教程(4)—內置函數(之四)"中__hash__()方法文檔)。為了在各種數值類型(int, float, decimal.Decimal以及fractions.Fraction)上易於實現和提高效率, Python對數值類型的哈希是基於一個為任意有理數定義的數學函數,因此適用於int和fractions.Fraction的所有實例,以及float和decimal.Decimal的所有有限實例。本質上,這個函數是由一個固定素數P的約簡模P給出的。P的值是由sys.hash_info的modulus屬性提供給Python的。

CPython實現細節:目前在32位C long機器上主要使用的是的P = 2**31 - 1,以及64位C long機器上的P = 2**61 - 1。

以下是具體的規則:

  • 如果x = m / n是一個非負的有理數並且n不能被P整除,則定義hash(x)為m * invmod (n, P) % P,其中invmod (n, P)給出了n模P的逆。
  • 如果x = m / n是一個非負的有理數並且n能被P整除(但m不能),那麼n沒有P的逆模,上面的規則不適用。在這種情況下,將hash(x)定義為常數值sys.hash_info.inf。
  • 如果x = m / n是負的有理數,則將hash(x)定義為-hash(-x)。如果得到的哈希值是-1,則將其替換為-2。
  • 特定的值sys.hash_info.inf,-sys.hash_info.inf和sys.hash_info.nan分別用作正無窮、負無窮或NaNs的散列值。(所有的可哈希的NaNs都有相同的哈希值。)
  • 對於複數z,哈希值是通過對實部和虛部的哈希值進行合併計算hash(z.real) + sys.hash_info.imag * hash(z.imag),然後對結果以2**sys.hash_info.width為模進行約簡運算,使其位於範圍(-2**(sys.hash_info.width-1),2**(sys.hash_info.width- 1))內。如果結果是-1,就用-2代替。

為了闡明上述規則,下面是一些Python代碼示例,相當於內置的散列,用於計算有理數、浮點數或複數的散列:

<code>import sys, math

def hash_fraction(m, n):
"""Compute the hash of a rational number m / n.

Assumes m and n are integers, with n positive.
Equivalent to hash(fractions.Fraction(m, n)).

"""
P = sys.hash_info.modulus
# Remove common factors of P. (Unnecessary if m and n already coprime.)
while m % P == n % P == 0:
m, n = m // P, n // P

if n % P == 0:
hash_value = sys.hash_info.inf
else:
# Fermat's Little Theorem: pow(n, P-1, P) is 1, so
# pow(n, P-2, P) gives the inverse of n modulo P.
hash_value = (abs(m) % P) * pow(n, P - 2, P) % P
if m < 0:
hash_value = -hash_value
if hash_value == -1:

hash_value = -2
return hash_value

def hash_float(x):
"""Compute the hash of a float x."""

if math.isnan(x):
return sys.hash_info.nan
elif math.isinf(x):
return sys.hash_info.inf if x > 0 else -sys.hash_info.inf
else:
return hash_fraction(*x.as_integer_ratio())

def hash_complex(z):
"""Compute the hash of a complex number z."""

hash_value = hash_float(z.real) + sys.hash_info.imag * hash_float(z.imag)
# do a signed reduction modulo 2**sys.hash_info.width
M = 2**(sys.hash_info.width - 1)
hash_value = (hash_value & (M - 1)) - (hash_value & M)
if hash_value == -1:
hash_value = -2
return hash_value/<code>


【結束】

篇尾寄語:萬丈高樓平地起,是否具有紮實的基礎決定一個人能否走遠以及能走多遠。Python的學習也是同樣的道理!


分享到:


相關文章: