数字
本提案定义了一个抽象基类(ABC)的层次结构(PEP 3119),用于表示类似数字的类。
它提议了一个层次结构,其中 Number :> Complex :> Real :> Rational :> Integral
存在三种不同的数字类型:整数、浮点数和复数。 此外,布尔值属于整数的子类型。
整数具有无限的精度。
浮点数通常使用 C 中的 double 来实现;有关你的程序运行所在机器上浮点数的精度和内部表示法可在 sys.float_info
中查看。
复数包含实部和虚部,分别以一个浮点数表示。 要从一个复数 z 中提取这两个部分,可使用 z.real
和 z.imag
。
整个数字类(整数、浮点数、复数、布尔值)都属于不可变类型(Immutable Type),即一旦创建,其值不能被修改。当变量重新赋值时,其实指向了新的内存地址。
整数
初学者通常只掌握十进制整数的定义方法,其实 Python 定义整数时支持四种进制格式:
# 十进制整数除了单独的0外,不能以0开头,否则会报语法错误
a = 17
# 16进制 使用 0x 或 0X 前缀
b = 0x11
# 二进制 使用 0b 或 0B 前缀
c = 0b10001
# 八进制 使用 0o 或 0O 前缀
d = 0o21
print(a == b) # True
print(a == c) # True
print(a == d) # True
print(0x11 is 17) # True 驻留机制
print(0x11 is 0b10001) # True
print(0x11 is 0o21) # True
# 对于负数,定义时使用 - 前缀,正数定义时可省略 + 前缀
e = -17
f = -0x11
g = -0b10001
h = -0o21
print(e == f) # True
print(e == g) # True
print(e == h) # True
# 为了提高可读性,各进制都可以在数字中使用下划线 _ 分隔。
print(1_000_000) # 1000000
print(0b1010_1010) # 170
x 不总是等于 +x
在Python中,几乎所有情况下x
总是等于 +x
。
特殊情况1:自定义类
如果类重载了 __pos__
方法,+x
的行为可以自定义,这时 x
可能不等于 +x
。
例如Python的collections.Counter
类重载了 __pos__
方法,所以+Counter()
的行为可以自定义。
特殊情况2:decimal Decimal 类型的“设计是基于考虑人类习惯的浮点数模型,并且因此具有以下最高指导原则 —— 计算机必须提供与人们在学校所学习的算术相一致的算术。” —— 摘自 decimal 算术规范描述。
Decimal 通过设置精度上下文,实现在精度内,数字可以完全精确地表示。
相比之下,1.1 和 2.2 这样的数字在二进制浮点形式下没有精确的表示。 最终用户通常不希望 1.1 + 2.2 像在二进制浮点形式下那样被显示为 3.3000000000000003。
import decimal
ctx = decimal.getcontext()
ctx.prec = 40 # 设置精度为40
one_third = decimal.Decimal('1') / decimal.Decimal('3')
print(one_third)
print(one_third == +one_third) # True
ctx.prec = 28 # 设置精度为28
print(one_third == +one_third) # False
int函数
可以通过int()
函数将字符串或数字转换为整数。
int函数签名:int(x, base=10) -> int
参数说明:
x
:要转换的字符串或数字base
:进制,默认为10
返回值:
- 返回一个整数
# 将字符串转换为整数
print(int('123')) # 123
# 将浮点数转换为整数,会舍弃小数部分
print(int(123.55)) # 123
# 将字符串转换为十六进制整数,base表示进制
print(int('11', base=16)) # 17
浮点数
# 只要进行了除法运算,就会产生浮点数。即便除数、被除数都是整数,也没有余数,商也会是浮点数。
print(10 / 2) # 5.0
# 使用科学计数法定义的都是浮点数,e 或 E 表示 10 的幂次。
print(1e1) # 10.0
print(2.5e3) # 2500.0
print(1E-2) # 0.01
float函数
可以通过float()
函数将字符串或数字转换为浮点数。
float函数签名:float(x) -> float
参数说明:
x
:要转换的字符串或数字
返回值:
- 返回一个浮点数
# 将字符串转换为浮点数
print(float('3.14')) # 3.14
# 将整数转换为浮点数
print(float(1)) # 1.0
浮点数存在一些特殊值,包括无穷大、负无穷大和NaN。
无穷大表示超出浮点数表示范围的值,NaN表示未定义或无法表示的浮点运算结果。
无穷大的运算遵循数学运算规则,NaN参与的运算结果为NaN,但NaN不等于任何值,包括自己,实际业务中往往是剔除或众数补全。
# 无穷大
float('inf')
# 负无穷大
float('-inf')
# NaN
float('nan')
# NaN与任何值(包括自己)都不相等,也是唯一不等于自身的值,
print(float('nan') == float('nan')) # False
print(float('nan') is float('nan')) # False
# 可以通过这点判断是否为NaN
is_nan = lambda x: x != x
print(is_nan(float('nan'))) # True
精度问题
浮点数精度问题源于二进制无法精确表示某些十进制的小数部分,这并非 Python 语言的问题,是由于二进制浮点表示的固有限制。
Python 的浮点数遵循 IEEE 754 双精度标准,使用64位存储,下面的代码输出清楚地说明了这一点。
import sys
print(sys.float_info)
"""
sys.float_info(
# 表示当前系统能够表示的最大浮点数,值为 1.7976931348623157e+308。这是双精度浮点数的最大值(IEEE 754 标准)。
max=1.7976931348623157e+308,
# 表示浮点数能够表示的最大指数值(以 2 为底),值为 1024。这是双精度浮点数的最大指数值。
max_exp=1024,
# 表示浮点数能够表示的最大指数值(以 10 为底),值为 308。这是双精度浮点数在十进制下的最大指数值。
max_10_exp=308,
# 表示当前系统能够表示的最小正浮点数(非零),值为 2.2250738585072014e-308。这是双精度浮点数的最小正数。
min=2.2250738585072014e-308,
# 表示浮点数能够表示的最小指数值(以 2 为底),值为 -1021。这是双精度浮点数的最小指数值。
min_exp=-1021,
# 表示浮点数能够表示的最小指数值(以 10 为底),值为 -307。这是双精度浮点数在十进制下的最小指数值。
min_10_exp=-307,
# 表示浮点数在十进制下的有效数字 位数,值为 15。这意味着双精度浮点数可以精确表示大约 15 位十进制数字。
dig=15,
# 表示浮点数的尾数位数(以 2 为底),值为 53。这是双精度浮点数的尾数位数(包括隐含的 1)。
mant_dig=53,
# 表示 1.0 和下一个可表示的浮点数之间的差值,值为 2.220446049250313e-16。这是双精度浮点数的机器精度。
epsilon=2.220446049250313e-16,
# 表示浮点数的基数(进制),值为 2。这意味着浮点数是基于二进制的。
radix=2,
# 表示浮点数的舍入模式,值为 1。这表示当前系统使用的是“舍入到最近偶数”的舍入模式(IEEE 754 默认模式)
rounds=1
)
"""
# 经典的浮点数精度问题
print(0.1 + 0.2) # 0.30000000000000004
print(0.1 + 0.2 == 0.3) # False
# 查看实际存储的值
from decimal import Decimal
print(Decimal(0.1)) # 0.1000000000000000055511151231257827021181583404541015625
print(Decimal(0.2)) # 0.200000000000000011102230246251565404236316680908203125
# 更复杂的例子
total = 0.0
for i in range(10):
total += 0.1
print(total) # 0.9999999999999999
print(total == 1.0) # False
# 因此永远不要直接比较浮点数是否相等,使用容差比较(允许容纳极小的误差)
def robust_equals(a, b, rel_tol=1e-09, abs_tol=1e-12):
"""
比较两个数值是否在给定的相对或绝对误差范围内相等。
参数:
a (float): 待比较的第一个数值。
b (float): 待比较的第二个数值。
rel_tol (float, optional): 相对误差容限,用于处理较大数值时的比例误差。默认为 1e-09。
abs_tol (float, optional): 绝对误差容限,用于处理接近零时的微小误差。默认为 1e-12。
返回:
bool: 如果 `a` 和 `b` 在给定的误差范围内相等,则返回 `True`,否则返回 `False`。
示例:
>>> robust_equals(1.0, 1.000000001)
True
>>> robust_equals(1.0, 1.0001)
False
"""
return abs(a - b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
print(robust_equals(1.0, 1.0001)) # False
# 等价于使用内置的isclose函数(推荐)
import math
print(math.isclose(1.0, 1.0001, rel_tol=1e-09, abs_tol=1e-12)) # False
浮点数陷阱与解决方案
浮点数所有陷阱的根源都是因为浮点数无法精确表示所有数,因此需要使用其他方式来存储数据,最常见的解决方案是使用Decimal
类型、字符串或整数。
import json
from decimal import Decimal
# 陷阱1: JSON序列化精度丢失
def json_precision_issue():
# 前因:字符串转Json、float会出现精度丢失现象,因此需要使用精度无限的整数或字符串来存储数据
response = '{"abc": 4.4257052820783003}'
print(float("4.4257052820783003")) # 4.4257052820783 精度丢失
print(json.loads(response)) # {'abc': 4.4257052820783} 精度丢失
print(json.dumps(json.loads(response))) # 精度丢失是永久的,之后无法还原
# 推荐写法
class DecimalEncoder(json.JSONEncoder):
"""重新序列化Decimal需要自定义编码器"""
def default(self, obj):
if isinstance(obj, Decimal):
# 将Decimal转换为字符串,精度不会丢失 如果使用float(obj)依然会丢失精度
return str(obj)
return super().default(obj)
# 使用Decimal解析字符串,精度不会丢失
decimal_json_str = json.loads(response, parse_float=Decimal)
# 使用自定义编码器重新序列化,确保转化后的数据精度不会丢失
print(json.dumps(decimal_json_str, cls=DecimalEncoder))
# {"abc": "4.4257052820783003"} 这是精度保持正常的输出结果
# 陷阱2: 循环累加误差
def accumulation_error_demo():
"""循环累加导致的误差积累"""
# 错误的方式
total = 0.0
for _ in range(1000000):
total += 0.1
print(f"Wrong way: {total}") # 不等于100000.0
# 正确的方式1:使用整数运算
total_cents = 0
for _ in range(1000000):
total_cents += 10 # 以分为单位
total = total_cents / 100
print(f"Correct way 1: {total}")
# 正确的方式2:使用Decimal
total = Decimal('0')
for _ in range(1000000):
total += Decimal('0.1')
print(f"Correct way 2: {total}")
# 陷阱3: 浮点数作为字典键
def float_key_problem():
"""浮点数作为字典键的问题"""
d = {}
key1 = 0.1 + 0.2
key2 = 0.3
d[key1] = "value1"
print(d.get(key2)) # None,因为key1 != key2
# 解决方案:使用精度无限的字符串或整数
d_safe = {}
d_safe[str(round(key1, 10))] = "value1"
print(d_safe.get(str(round(key2, 10)))) # "value1"
# 陷阱4: 范围判断
def range_check_issue():
"""浮点数范围判断的陷阱"""
values = []
x = 0.0
while x <= 1.0:
values.append(x)
x += 0.1
print(f"Values count: {len(values)}") # 是11个而不是期望的10个
print(f"Last value: {values[-1]}") # 0.9999999999999999
print(f"Last value <= 1.0: {values[-1] <= 1.0}") # True
复数
复数(Complex Number)是数学中的一种数,表示为
其中a
和b
是实数,i
是虚数单位,是可以与实数在一起按照同样的运算律进行四则运算,满足的数。
复数是Python的内置数字类型,在科学计算、信号处理、控制系统等领域有广泛应用。这里只介绍复数的基本操作。
complex函数
complex函数签名:complex(real, imag=0) -> complex
参数说明:
real
:实部imag
:虚部,默认为0
返回值:
- 返回一个复数
# 使用complex()函数创建复数
z1 = complex(3, 4) # 3+4j
z2 = complex('3+4j') # 从字符串创建
z3 = complex(3) # 3+0j
# 使用j或J作为虚数单位
z4 = 3 + 4j
z5 = 3 + 4J # j和J等价
z6 = 4j # 纯虚数
z7 = 3 + 0j # 实数的复数表示
复数的属性和基本操作
z = 3 + 4j
# 基本属性
print(f"实部: {z.real}") # 3.0
print(f"虚部: {z.imag}") # 4.0
print(f"共轭: {z.conjugate()}") # (3-4j)
布尔类型
布尔类型是整数类型的子类,只有True和False两种值。
bool函数
bool(x)
函数可以将任意值转换为布尔值。
函数签名:bool(x) -> bool
参数说明:
x
:要转换的值
返回值:
- 返回一个布尔值
如果x
是数字类型,则判断x
是否为0,为0则返回False,否则返回True。
如果x
是布尔类型,则返回x
。
默认情况下,用户定义的类的实例都为True。除非实现了__bool__
或__len__
方法。
简单来说,bool(x)
调用x.__bool__()
方法,如果__bool__
方法返回False,则bool(x)
返回False,否则返回True。
如果没有实现__bool__
方法,则调用__len__
方法,如果__len__
方法返回0,则bool(x)
返回False,否则返回True。
常见的布尔值为False的情况:
# 是数字类型且为0的值
print(bool(0)) # False
print(bool(0.0)) # False
print(bool(0j)) # False
# 是布尔类型且为False的值
print(bool(False)) # False
# 实现了__bool__方法且返回False的值
print(bool(None)) # False
# 没有实现__bool__方法且__len__方法返回0的值,例如:
print(bool("")) # False - 空字符串
print(bool([])) # False - 空列表
print(bool({})) # False - 空字典
print(bool(set())) # False - 空集合
其他的数值布尔值为True。
布尔运算
and运算真值表
A | B | A and B |
---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
or运算真值表
A | B | A or B |
---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
not运算真值表
A | not A |
---|---|
True | False |
False | True |
布尔类型数字性质
# 布尔值参与运算时,可被当作整数使用,True为1,False为0
print(True is 1) # False 因为True和1是不同的对象
print(True == 1) # True 因为True和1的值相等
print(True + True) # 2
print(False * 10) # 0
print(True / 2) # 0.5
# 布尔值的类型关系
print(isinstance(True, int)) # True
print(isinstance(True, bool)) # True
print(type(True)) # <class 'bool'>
# 布尔值在列表中的计数
values = [True, False, True, True, False]
true_count = sum(values) # 3
false_count = len(values) - true_count # 2
print(f"True的个数: {true_count}, False的个数: {false_count}")
内置函数
abs函数
返回一个数字的绝对值。 参数可以是整数、浮点数或任何实现了 abs() 的对象。
函数签名:abs(x) -> number
参数说明:
x
:要计算绝对值的数字
返回值:
- 返回一个数字的绝对值。如果参数是一个复数,则返回它的模。
print(abs(-10)) # 10
print(abs(10.5)) # 10.5
print(abs(3+4j)) # 5.0 - 复数的模长,即sqrt(3²+4²)