note-PythonCookbook-第三章 数字日期和时间

第三章 数字日期和时间

3.1 数字的四舍五入

简单的舍入运算使用 round() 函数即可:

>>> round(1.2345, 1)
1.2
>>> round(1.2345, 3)
1.234
>>> round(31.2345, 0)
31.0
>>> round(31.2345, -1)
30.0

round() 的第二位参数 ndigits,表示精度,可以为负。
当被舍入小数刚好在两个边界中间时,舍入结果根据前一位的值遵循奇进偶舍的原则。
python 的浮点数存储规则会导致微小的误差存在,所以有时肉眼看到的值为 5 时,并非边界中间。

>>> round(4.5)
4
>>> round(4.45, 1)
4.5
>>> from decimal import Decimal
>>> Decimal(4.5)
Decimal('4.5')
>>> Decimal(4.45)
Decimal('4.45000000000000017763568394002504646778106689453125')

3.2 执行精确的浮点数运算

python 的浮点数据类型使用底层表示,受底层浮点单位的影响,浮点运算有时会产生不可避免的误差。

>>> 6.8 * 9
61.199999999999996

decimal 模块可以避免这种误差。

>>> from decimal import Decimal
>>> a = Decimal('6.8')
>>> a * 9
Decimal('61.2')

Decimal 初始化的对象是浮点数时,能看到该浮点数的实际值。小数点后17位后才出现误差,实际使用中很少涉及这么大的精度,而且使用这种原生浮点运算速度会快。

>>> Decimal(1.1)
Decimal('1.100000000000000088817841970012523233890533447265625')
>>> 1.22e+18 == (1.22e+18 + 1)
True
>>> 1.22e+18 - 1.22e+18 + 1
1.0
>>> 1.22e+18 - (1.22e+18 + 1)
0.0

3.3 数字的格式化输出

内置的 format() 函数能控制位数、对齐、千位分隔符。格式化输入规则如下:
[[填充符]对齐方式][符号][#][0][宽度][千位分隔符][小数位数][类型]

  • 对齐方式:’<’,左对齐;’>’,右对齐;’^’,居中对齐;’=’,对齐到正负号后面,只能对数值使用。
  • 符号:’+’,整数显示正号,负数显示负号;’-’,只有负数显示负号(默认);’ ',正数前面会留一个空格,负数前面是负号。
  • 宽度:正整数。
  • 千位分隔符:’,’ 或 ‘_’。
  • 小数位数:正整数。

3.4 二八十六进制整数

bin(),oct(),hex() 分别把整数转换为二进制、八进制、十六进制的字符串,输出结果带有前缀 0b,0o,0x。
不想要前缀,可以使用 format() 函数,使用 ’b‘,‘o’,‘x’ 指定类型。

3.5 字节到大整数的打包与解包

字节字符串到整数:

>>> data = bytes('一二三', encoding='utf8')
>>> int.from_bytes(data, 'big')
4219152526395654322313
>>> int.from_bytes(data, 'little')
2540526866430202788068

大整数转换为字节字符串:

>>> x = 4219152526395654322313
>>> x.to_bytes(16, 'big')
b'\x00\x00\x00\x00\x00\x00\x00\xe4\xb8\x80\xe4\xba\x8c\xe4\xb8\x89'
>>> x.to_bytes(16, 'little')
b'\x89\xb8\xe4\x8c\xba\xe4\x80\xb8\xe4\x00\x00\x00\x00\x00\x00\x00'

很大的整数打包为字节字符串时,可以使用 bit_length() 方法来确定位数。

x.to_bytes(x.bit_length(), 'big')

3.6 复数的数学运算

复数可以用 complex() 或带有后缀 j 的浮点数来指定。
real 和 imag 属性用来获取其实部和虚部,conjugate() 方法获取其共轭复数。
加减乘除等常见运算可以直接操作,但是正弦、余弦、平方根等运算需要使用 cmath 模块。

>>> import cmath
>>> cmath.cos(3+5j)
(-73.46729221264526-10.471557674805572j)
>>> cmath.sqrt(-1)   # math.sqrt(-1) 无法计算
1j

3.7 无穷大与 NaN

这两种特殊的值能通过 float() 创建:

>>> a = float('inf')
>>> a
inf
>>> b = float('-inf')
-inf
>>> c = float('nan')
>>> c
nan

测试这些值的存在要使用 math 模块的 isinf() 和 isnan() 来判定:

>>> math.isinf(a)
True

无穷大在数学计算时会传播,

>>> a + 1
inf
>>> 100 / a
0.0

有些未定义的运算返回 nan:

>>> a - a
nan
>>> a / a
nan

nan 值在所有操作中都会传播,并且不会异常:

>>> c + 6
nan
>>> c / 6
nan
>>> c * 6
nan

nan 的特别的地方是比较操作:

>>> c == c
False
>>> c is c
True
>>> c is float('nan')
False

因此,判定 nan 值的唯一方法只能是 math.isnan() 方法。

3.8 分数运算

fractions 模块能用来执行分数运算。

>>> from fractions import Fraction
>>> a = Fraction(3, 4)
>>> a
Fraction(3, 4)
>>> print(a)
3/4
>>> a.numerator		# 分子
3
>>> a.denominator	# 分母
4
>>> (a*a).limit_denominator(8)		# 限制分母大小
Fraction(4, 7)

3.9 大型数组运算

numpy 库能创建数组对象,其运算和列表不同。

  • 标量运算会作用在数组中的每一个元素上;
  • 数组之间的运算作用在对等位置的元素上;
  • 提供了类似 math 模块的通用函数(平方根、三角函数等),计算起来比迭代使用 math 的函数快;
  • 容易建立大型数组;支持索引操作;支持条件选择。

3.10 矩阵与线性代数运算

numpy 库的 matrix 对象类似数组对象,但是遵循线性代数的计算规则。

import numpy as np
>>> a = np.array([[1,1,1],[2,2,2],[3,3,3]])
>>> b = np.array([[4],[4],[4]])
>>> a * b
array([[ 4,  4,  4],
       [ 8,  8,  8],
       [12, 12, 12]])
>>> a = np.matrix(a)
>>> b = np.matrix(b)
>>> a * b
matrix([[12],
        [24],
        [36]])

使用 a.I 获得矩阵 a 的逆矩阵,a.T 获得矩阵 a 的转置矩阵。
在 numpy 的 linalg 中有更多的操作矩阵的函数。

3.11 随机选择

random 模块

  • random.choice(序列),从序列中随机取一个元素;
  • random.sample(序列, 个数),从总体序列中随机取指定个数的样本;
  • random.shuffle(序列),打乱序列中元素顺序;
  • random.randint(a, b),生成区间 [a, b] 内的随机整数;
  • random.random(),生成区间 [0, 1) 之间的随机浮点数;
  • random.seed(),修改初始化种子;
  • 用于根据正态分布、高斯分布等生成随机数;

random 模块不要用在和密码学相关的程序中。

3.12 基本的日期与时间转换

datetime 模块的 timedelta 可以完成天、时、分、秒的单位换算:

>>> from datetime import timedelta
>>> a = timedelta(hours=3.2)
>>> a
datetime.timedelta(seconds=11520)

timedelta 实例可以和 datetime 实例做加减操作。
对于更复杂的时间操作,可以使用 dateutil 模块

3.13 计算最后一个周五的日期

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

使用第三方包 dateutil 能更简单地实现:

>>> from datetime import datetime
>>> from dateutil.relativedelta import relativedelta
>>> from dateutil.rrule import *
>>> d = datetime.now()
>>> d + relativedelta(weekday=FR)	# 下一个周五
datetime.datetime(2020, 4, 10, 18, 45, 17, 665903)
>>> d + relativedelta(weekday=FR(-1))	# 上一个周五
datetime.datetime(2020, 4, 3, 18, 45, 17, 665903)

3.14 计算当前月份的日期范围

calendar 模块的 monthrange(year, month),返回输入月份的第一天的星期和该月天数。
datetime 和 date 实例的 replace() 方法可以返回指定某项时间的 datetime 或 date 实例。

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)

3.15 字符串转换为日期

datetime 模块的 strptime(text, format) 方法可以把日期格式的字符串转为 datetime 实例。
strptime 方法效率很低,如果要处理大量格式统一的日期字符串,自己写一个方法转换效率更高。

3.16 结合时区的日期操作

pytz 模块。
(读完了,作者备注了一句 pytz 可能过时了-_-)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值