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的学习也是同样的道理!


分享到:


相關文章: