目录 |
| 目录 |
|
---|---|---|---|
数字日期和时间(一) | 1.数字的四舍五入 2.执行精确的浮点数运算 3.数字的格式化输出 4.二、八、十六进制整数 | 数字日期和时间(三) | 9.大型数组运算 10.矩阵与线性代数运算 11.随机选择 12.基本的日期与时间转换 |
数字日期和时间(二) | 5.字节到大整数的打包与解包 6.复数的数学运算 7.无穷大与 NaN 8.分数运算 | 数字日期和时间(四) | 13.计算上一个周五的日期 14.计算当前月份的日期范围 15.字符串转换为日期 16.结合时区的日期操作 |
数字日期和时间(二)
5.字节到大整数的打包与解包
你有一个字节字符串并想将它解压成一个整数。或者,你需要将一个大整数转换为一个字节字符串。
假设你的程序需要处理一个拥有 128 位长的 16 个元素的字节字符串。比如:
data = b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
为了将 bytes
解析为整数,使用 int.from_bytes()
方法,并像下面这样指定字节顺序:
>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>>
little
: 小端序(最低有效字节在前)。big
: 大端序(最高有效字节在前)。
为了将一个大整数转换为一个字节字符串,使用 int.to_bytes()
方法,并像下面这样指定字节数和字节顺序:
>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'big')
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> x.to_bytes(16, 'little')
b'4\x00#\x00\x01\xef\xcd\x00\xab\x90x\x00V4\x12\x00'
>>>
大整数和字节字符串之间的转换操作并不常见。然而,在一些应用领域有时候也会出现,比如密码学或者网络。例如,IPv6 网络地址使用一个 128 位的整数表示。如果你要从一个数据记录中提取这样的值的时候,你就会面对这样的问题。
作为一种替代方案,你可能想使用 struct
模块来解压字节。这样也行得通,不过利用 struct
模块来解压对于整数的大小是有限制的。因此,你可能想解压多个字节串并将结果合并为最终的结果,就像下面这样:
>>> data
b'\x00\x124V\x00x\x90\xab\x00\xcd\xef\x01\x00#\x004'
>>> import struct
>>> hi, lo = struct.unpack('>QQ', data)
>>> (hi << 64) + lo
94522842520747284487117727783387188
>>>
字节顺序规则(little
或 big
)仅仅指定了构建整数时的字节的低位高位排列方式。我们从下面精心构造的 16 进制数的表示中可以很容易的看出来:
>>> x = 0x01020304
>>> x.to_bytes(4, 'big')
b'\x01\x02\x03\x04'
>>> x.to_bytes(4, 'little')
b'\x04\x03\x02\x01'
>>>
如果你试着将一个整数打包为字节字符串,那么它就不合适了,你会得到一个错误。如果需要的话,你可以使用 int.bit_length()
方法来决定需要多少字节位来存储这个值。
>>> 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 # 如果有余数, 则需补 1 字节
...
>>>
>>> x.to_bytes(nbytes, 'little')
b'\x03X\xf1\x82iT\x96\xac\xc7c\x16\xf3\xb9\xcf...\xd0'
>>>
6.复数的数学运算
你写的最新的网络认证方案代码遇到了一个难题,并且你唯一的解决办法就是使用复数空间。再或者是你仅仅需要使用复数来执行一些计算操作。
复数可以用使用函数 complex(real, imag)
或者是带有后缀 j
的浮点数来指定。比如:
>>> a = complex(2, 4)
>>> b = 3 - 5j
>>> a
(2+4j)
>>> b
(3-5j)
>>>
对应的实部、虚部和共轭复数可以很容易的获取。就像下面这样:
>>> a.real
2.0
>>> a.imag
4.0
>>> a.conjugate()
(2-4j)
>>>
另外,所有常见的数学运算都可以工作:
>>> a + b
(5-1j)
>>> a * b
(26+2j)
>>> a / b
(-0.4117647058823529+0.6470588235294118j)
>>> abs(a)
4.47213595499958
>>>
如果要执行其他的复数函数比如正弦、余弦或平方根,使用 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 中大部分与数学相关的模块都能处理复数。比如如果你使用 numpy
,可以很容易的构造一个复数数组并在这个数组上执行各种操作:
>>> 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 的标准数学函数确实情况下并不能产生复数值,因此你的代码中不可能会出现复数返回值。比如:
>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>
如果你想生成一个复数返回结果,你必须显示的使用 cmath
模块,或者在某个支持复数的库中声明复数类型的使用。比如:
>>> import cmath
>>> cmath.sqrt(-1)
1j
>>>
7.无穷大与 NaN
你想创建或测试正无穷、负无穷或 NaN(非数字)的浮点数。
Python 并没有特殊的语法来表示这些特殊的浮点值,但是可以使用 float()
来创建它们。比如:
>>> a = float('inf')
>>> b = float('-inf')
>>> c = float('nan')
>>> a
inf
>>> b
-inf
>>> c
nan
>>>
为了测试这些值的存在,使用 math.isinf()
和 math.isnan()
函数。比如:
>>> math.isinf(a)
True
>>> math.isnan(c)
True
>>>
想了解更多这些特殊浮点值的信息,可以参考 IEEE 754 规范。然而,也有一些地方需要你特别注意,特别是跟比较和操作符相关的时候。
无穷大数在执行数学计算的时候会传播,比如:
>>> a = float('inf')
>>> a + 45
inf
>>> a * 10
inf
>>> 10 / a
0.0
>>>
但是有些操作时未定义的并会返回一个 NaN 结果。比如:
>>> a = float('inf')
>>> a/a
nan
>>> b = float('-inf')
>>> a + b
nan
>>>
NaN 值会在所有操作中传播,而不会产生异常。比如:
>>> 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
>>>
由于这个原因,测试一个 NaN 值得唯一安全的方法就是使用 math.isnan()
,也就是上面演示的那样。
有时候程序员想改变 Python 默认行为,在返回无穷大或 NaN 结果的操作中抛出异常。fpectl
模块可以用来改变这种行为,但是它在标准的 Python 构建中并没有被启用,它是平台相关的,并且针对的是专家级程序员。
8.分数运算
你进入时间机器,突然发现你正在做小学家庭作业,并涉及到分数计算问题。或者你可能需要写代码去计算在你的木工工厂中的测量值。
fractions
模块可以被用来执行包含分数的数学运算。比如:
>>> from fractions import Fraction
>>> a = Fraction(5, 4)
>>> b = Fraction(7, 16)
>>> print(a + b)
27/16
>>> print(a * b)
35/64
>>> # Getting numerator/denominator
>>> c = a * b
>>> c.numerator
35
>>> c.denominator
64
>>> # Converting to a float
>>> float(c)
0.546875
>>> # Limiting the denominator of a value
>>> print(c.limit_denominator(8))
4/7
>>> # Converting a float to a fraction
>>> x = 3.75
>>> y = Fraction(*x.as_integer_ratio())
>>> y
Fraction(15, 4)
>>>
x.as_integer_ratio()
:将浮点数x
转换为一个整数比例元组(numerator, denominator)
。*
操作符:将元组解包为单独的参数(等价于Fraction(15, 4)
)。
在大多数程序中一般不会出现分数的计算问题,但是有时候还是需要用到的。比如,在一个允许接受分数形式的测试单位并以分数形式执行运算的程序中,直接使用分数可以减少手动转换为小数或浮点数的工作。