一、decimal模块
Decimal类的基本使用
概念定义
Decimal是Python标准库decimal模块提供的类,用于高精度的十进制浮点数运算。与内置的float类型不同,Decimal可以精确表示十进制数,避免了二进制浮点数常见的精度问题。
使用场景
- 金融计算(需要精确的货币计算)
- 科学计算(需要高精度结果)
- 任何需要避免浮点数精度误差的场景
常见误区或注意事项
- Decimal对象需要通过字符串初始化才能保证精确性,直接使用浮点数初始化仍会引入精度问题
- Decimal运算比float慢,在不需要精确计算的场景不应使用
- 需要设置合适的精度(通过getcontext().prec)
示例代码
from decimal import Decimal, getcontext
# 正确初始化方式
a = Decimal('0.1')
b = Decimal('0.2')
print(a + b) # 输出: 0.3
# 错误初始化方式(仍会有精度问题)
c = Decimal(0.1)
d = Decimal(0.2)
print(c + d) # 输出: 0.3000000000000000166533453694
# 设置精度
getcontext().prec = 6
e = Decimal('1') / Decimal('7')
print(e) # 输出: 0.142857
# 金融计算示例
price = Decimal('19.99')
quantity = Decimal('3')
total = price * quantity
print(total) # 输出: 59.97
精度设置方法(getcontext().prec)
概念定义
getcontext().prec
是 Python 中 decimal
模块提供的用于设置十进制运算精度的属性。它决定了十进制数在进行算术运算时保留的有效数字位数(不包括前导零)。
使用场景
- 需要高精度计算的金融、科学计算领域
- 避免浮点数精度问题导致的误差
- 需要控制计算结果的有效数字位数
注意事项
prec
设置的是有效数字位数,不是小数位数- 设置会影响所有后续的
Decimal
运算 - 默认精度为 28 位
- 更高的精度会带来更大的计算开销
示例代码
from decimal import getcontext, Decimal
# 设置精度为5位有效数字
getcontext().prec = 5
# 计算结果将保留5位有效数字
result = Decimal(1) / Decimal(7)
print(result) # 输出: 0.14286
# 设置更高精度
getcontext().prec = 10
result = Decimal(1) / Decimal(7)
print(result) # 输出: 0.1428571429
舍入模式
概念定义
舍入模式是指在进行数值四舍五入时采用的规则。Python中常用的舍入模式包括:
ROUND_HALF_UP
:四舍五入(最常用)ROUND_DOWN
:直接截断ROUND_UP
:远离零方向舍入ROUND_HALF_EVEN
:银行家舍入法(四舍六入五成双)
使用场景
- 财务计算(需要精确到分)
- 科学计算(控制精度)
- 数据统计(统一数据格式)
常见误区
- 误以为所有语言都默认使用
ROUND_HALF_UP
- 浮点数精度问题(建议使用Decimal模块)
- 混淆不同舍入模式的效果
示例代码
from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN
# 传统round函数(使用ROUND_HALF_EVEN)
print(round(2.5)) # 2(银行家舍入)
print(round(3.5)) # 4
# 使用Decimal精确控制
num = Decimal('1.235')
print(num.quantize(Decimal('0.01'), ROUND_HALF_UP)) # 1.24
print(num.quantize(Decimal('0.01'), ROUND_HALF_EVEN)) # 1.24
num = Decimal('1.245')
print(num.quantize(Decimal('0.01'), ROUND_HALF_UP)) # 1.25
print(num.quantize(Decimal('0.01'), ROUND_HALF_EVEN)) # 1.24
二、fractions模块
Fraction类的创建方式
概念定义
Fraction类是Python标准库fractions
模块中的一个类,用于精确表示分数(有理数)。它可以帮助避免浮点数运算带来的精度问题。
使用场景
- 需要精确表示分数(如1/3而不是0.333…)
- 财务计算等需要高精度的场景
- 数学运算中需要保持分数形式
常见创建方式
- 通过分子分母创建
from fractions import Fraction
f = Fraction(3, 4) # 表示3/4
- 通过浮点数创建
f = Fraction(0.5) # 自动转换为1/2
- 通过字符串创建
f = Fraction('3/4') # 表示3/4
f = Fraction('0.25') # 表示1/4
- 通过另一个Fraction对象创建
f1 = Fraction(1, 2)
f2 = Fraction(f1) # 创建f1的副本
注意事项
- 分母不能为0,否则会抛出ZeroDivisionError
- 创建时分子分母会自动约分
- 从浮点数创建时可能会有微小误差(如Fraction(0.1)不完全等于1/10)
- 字符串形式必须符合"分子/分母"或十进制格式
示例代码
from fractions import Fraction
# 不同创建方式示例
print(Fraction(16, -10)) # 输出: -8/5 (自动约分)
print(Fraction(0.25)) # 输出: 1/4
print(Fraction('3/7')) # 输出: 3/7
print(Fraction('0.1')) # 输出: 1/10
分数运算与简化
概念定义
分数运算是指对分数进行加、减、乘、除等数学运算的过程。分数简化则是将分数约分到最简形式,即分子和分母没有公因数(除了1)。
使用场景
- 在科学计算、工程计算中处理精确值
- 游戏开发中处理比例和概率
- 金融计算中处理利率和比率
- 教育类应用中展示数学运算过程
常见误区或注意事项
- 进行加减运算前必须先通分
- 约分时要找到最大公约数(GCD)
- 除法运算要转换为乘以倒数
- 注意处理分母为零的情况
- 结果应始终表示为最简分数
示例代码
import fractions
from math import gcd
# 创建分数
f1 = fractions.Fraction(3, 4)
f2 = fractions.Fraction(1, 6)
# 基本运算
print(f1 + f2) # 11/12
print(f1 - f2) # 7/12
print(f1 * f2) # 1/8
print(f1 / f2) # 9/2
# 手动简化分数
def simplify_fraction(numerator, denominator):
common_divisor = gcd(numerator, denominator)
return numerator // common_divisor, denominator // common_divisor
# 示例:简化18/24
simplified = simplify_fraction(18, 24)
print(f"简化后的分数: {simplified[0]}/{simplified[1]}") # 输出: 3/4
浮点数的转换
概念定义
浮点数转换是指将其他数据类型(如整数、字符串)转换为浮点数(float)类型的过程。在Python中,可以使用float()
函数进行转换。
使用场景
- 从用户输入(通常是字符串)中获取数值并进行数学计算
- 处理包含小数的数据
- 需要更高精度的数值运算时
常见误区或注意事项
- 不是所有字符串都能转换为浮点数(如"abc"会引发ValueError)
- 某些看似简单的浮点数在计算机中可能有精度问题(如0.1+0.2不等于0.3)
- 大整数转换为浮点数时可能丢失精度
示例代码
# 字符串转浮点数
num_str = "3.14"
num_float = float(num_str)
print(num_float) # 输出: 3.14
# 整数转浮点数
num_int = 42
num_float = float(num_int)
print(num_float) # 输出: 42.0
# 科学计数法转换
sci_num = "1.23e-4"
num_float = float(sci_num)
print(num_float) # 输出: 0.000123
# 错误示例
try:
invalid = float("python")
except ValueError as e:
print(f"错误: {e}") # 输出: 错误: could not convert string to float: 'python'
三、精度控制对比
decimal与fractions的精度差异
概念定义
- decimal模块:用于高精度的十进制浮点运算,适用于需要精确小数计算的场景(如金融计算)。
- fractions模块:用于精确表示分数,避免浮点数运算中的精度损失。
使用场景
- decimal:适合处理货币、科学计算等需要精确小数位的场景。
- fractions:适合处理分数运算(如1/3),避免浮点数精度问题。
常见误区
- decimal并非完全精确:虽然比float更精确,但仍受限于设置的精度位数。
- fractions不适用于所有场景:对于无理数(如π)或复杂小数,fractions可能无法精确表示。
示例代码
from decimal import Decimal, getcontext
from fractions import Fraction
# decimal示例
getcontext().prec = 6 # 设置6位精度
a = Decimal('1.1') + Decimal('2.2')
print(a) # 输出: 3.3
# fractions示例
b = Fraction(1, 3) + Fraction(1, 6)
print(b) # 输出: 1/2
适用场景选择
概念定义
适用场景选择是指在编程中根据不同的需求或条件,选择最适合的编程结构、算法或技术方案。在Python中,这通常涉及对多种编程结构(如循环、条件判断、函数等)的选择和优化。
使用场景
-
循环选择:
- 当需要遍历已知长度的序列(如列表、元组)时,优先使用
for
循环。 - 当需要根据条件重复执行代码块时,优先使用
while
循环。
- 当需要遍历已知长度的序列(如列表、元组)时,优先使用
-
条件判断选择:
- 简单的二元条件判断(如
if-else
)适合使用if
语句。 - 多重条件判断(如多个
elif
)适合使用字典映射或match-case
(Python 3.10+)。
- 简单的二元条件判断(如
-
函数选择:
- 重复使用的代码块适合封装为函数。
- 简单的逻辑操作可以使用匿名函数(
lambda
)。
常见误区或注意事项
- 避免过度使用嵌套循环或条件判断,这会导致代码可读性降低。
- 不要为了追求简洁而牺牲代码的可读性(如过度使用
lambda
)。 - 在性能敏感的场景中,优先选择时间复杂度更低的算法或数据结构。
示例代码
# 适用场景选择示例:循环选择
numbers = [1, 2, 3, 4, 5]
# 使用for循环遍历列表
for num in numbers:
print(num)
# 使用while循环实现计数器
count = 0
while count < 5:
print(count)
count += 1
# 适用场景选择示例:条件判断选择
value = 10
# 简单的if-else
if value > 5:
print("大于5")
else:
print("小于等于5")
# 多重条件判断(使用字典映射替代多个elif)
def handle_case_1():
print("处理情况1")
def handle_case_2():
print("处理情况2")
handlers = {
1: handle_case_1,
2: handle_case_2,
}
handlers.get(value, lambda: print("默认处理"))()
性能比较
概念定义
性能比较是指通过量化指标(如执行时间、内存占用等)来评估不同代码实现或算法在相同条件下的运行效率差异。在Python中,常用于比较不同实现方式的优劣。
使用场景
- 选择最优算法时(如排序算法对比)
- 评估不同库的性能差异(如
requests
vsurllib
) - 优化关键代码路径前确定瓶颈
- 验证代码优化效果时
常见误区
- 单次测试:未考虑系统波动,应多次测试取平均值
- 忽略环境:未控制测试环境的一致性(如后台进程干扰)
- 过早优化:在非关键路径上进行不必要的优化
- 错误指标:选择了不相关的比较指标(如用内存比较排序速度)
示例代码
import timeit
# 比较列表生成方式
def test_for_loop():
return [i for i in range(1000)]
def test_list():
return list(range(1000))
# 各执行10000次,比较耗时
t1 = timeit.timeit(test_for_loop, number=10000)
t2 = timeit.timeit(test_list, number=10000)
print(f"列表推导式: {t1:.4f}秒")
print(f"list()转换: {t2:.4f}秒")
print(f"性能差异: {abs(t1-t2)/t1*100:.1f}%")
更精确的测试方法
from timeit import repeat
trials = repeat(
stmt="sorted(data)",
setup="import random; data=[random.random() for _ in range(1000)]",
repeat=5,
number=1000
)
print(f"最佳耗时: {min(trials):.4f}秒")