尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Python分数处理实战:fractions模块精确计算与避坑指南

Python分数处理实战:fractions模块精确计算与避坑指南 1. 项目概述为什么Python的分数处理值得深究在编程世界里处理数字是天经地义的事整数、浮点数大家用得滚瓜烂熟。但一提到“分数”很多Python开发者甚至是有几年经验的朋友可能第一反应是“这还不简单用小数或者直接除不就行了” 我最初也是这么想的直到在一个财务精度计算的项目里踩了坑。我们当时需要精确计算一系列费率的分成比例比如1/3用浮点数0.3333333333333333去累加三次结果并不是1而是0.9999999999999999。就是这点微小的误差在批量处理和财务对账时引发了连锁问题最后不得不回溯数据加班重算。这个经历让我彻底明白Python内置的fractions模块分数模块绝不是可有可无的玩具而是处理需要绝对精确比例关系的利器。无论是学生成绩统计、游戏中的概率计算、音乐节拍表示还是像我们遇到的那种金融计算只要涉及“部分与整体”的精确关系分数就能大显身手。它帮你绕开二进制浮点数那些恼人的精度陷阱用最直观的数学方式表达和运算。所以今天我们不聊那些浮于表面的“Hello World”而是深入Python的“分数”世界。我会结合自己踩过的坑和实战经验带你从fractions.Fraction的基本用法一路走到如何用它解决真实场景下的复杂问题比如在一组成绩里快速找到最高分或者构建一个健壮的分数计算工具。你会发现这个看似简单的模块用好了能给你的代码带来意想不到的可靠性和简洁性。2. 核心模块解析fractions.Fraction的完全指南Python标准库中的fractions模块为我们提供了Fraction类专门用于表示分数。它的核心思想是将一个分数表示为分子和分母两个整数所有运算都基于整数进行从而保证绝对精确。2.1 创建Fraction对象的四种姿势创建分数对象非常灵活你可以根据手头的数据类型选择最方便的方式。方式一直接传入分子和分母整数这是最直接、最常用的方法。from fractions import Fraction # 创建分数 3/4 f1 Fraction(3, 4) print(f1) # 输出3/4 # 创建分数 -2/5分母为负分数整体为负 f2 Fraction(-2, 5) print(f2) # 输出-2/5 # 创建分数 2/-5分子为负分数整体为负 f3 Fraction(2, -5) print(f3) # 输出-2/5注意分母不能为零否则会引发ZeroDivisionError。创建后Fraction会自动将分数化为最简形式。例如Fraction(6, 8)会被自动简化为3/4。方式二传入一个浮点数当你有一个浮点数但又想获得其精确的分数表示时可以使用这种方式。但这里有个大坑需要警惕。f_from_float Fraction(0.75) print(f_from_float) # 输出3/4 f_from_float2 Fraction(0.1) print(f_from_float2) # 输出3602879701896397/36028797018963968看到第二个例子了吗0.1在二进制中是一个无限循环小数所以Fraction捕获的是浮点数0.1在内存中的精确二进制表示值而不是我们数学意义上的1/10。这通常不是我们想要的。方式三传入一个字符串这是最推荐的创建方式之一尤其是从用户输入或文件读取数据时。它能直接解析数学意义上的分数。f_from_str1 Fraction(3/4) print(f_from_str1) # 输出3/4 f_from_str2 Fraction(0.1) print(f_from_str2) # 输出1/10 f_from_str3 Fraction(-2/5) print(f_from_str3) # 输出-2/5用字符串创建Fraction会直接进行十进制解析完美避免了浮点数的精度问题。对于小数0.1它能正确识别为1/10。方式四传入另一个Fraction或Decimal对象这主要用于复制或转换。f_copy Fraction(f1) # f1是之前创建的 3/4 print(f_copy) # 输出3/4 from decimal import Decimal f_from_decimal Fraction(Decimal(0.125)) print(f_from_decimal) # 输出1/8使用Decimal类型创建也能获得精确的结果因为Decimal本身就是为了高精度十进制计算而设计的。实操心得创建方式的选择首选字符串形式当分数来源是文本如用户输入、配置文件、数据库文本字段时直接用字符串创建。安全、精确、无歧义。明确知道整数分子分母时直接使用两个整数参数。尽量避免从浮点数创建除非你非常清楚该浮点数的二进制表示及其影响并且接受由此产生的“近似”分数。在需要将浮点数转为分数时一个常见的技巧是先将其转换为字符串Fraction(str(0.1))。利用limit_denominator()方法如果你不得不从一个浮点数构造一个“近似”的、但分母大小合理的分数可以使用这个方法。例如Fraction(0.3333333333333333).limit_denominator(100)会返回1/3它会在分母不超过100的范围内找到一个最接近原浮点数的分数。2.2 分数的属性与基本操作创建了分数对象我们就可以像使用普通数字一样使用它们。访问分子与分母每个Fraction对象都有numerator分子和denominator分母两个属性返回的是化简后的整数。f Fraction(12, 18) # 会自动化简为 2/3 print(f.numerator) # 输出2 print(f.denominator) # 输出3算术运算支持加()、减(-)、乘(*)、除(/)、整除(//)、取余(%)、乘方(**)等所有标准算术运算符。运算结果会自动化简。a Fraction(1, 3) b Fraction(1, 6) print(a b) # 输出1/2 print(a - b) # 输出1/6 print(a * b) # 输出1/18 print(a / b) # 输出2/1 (即2) print(a ** 2) # 输出1/9比较运算支持所有比较运算符,!,,,,。比较是基于分数的数值大小进行的。print(Fraction(1, 2) Fraction(1, 3)) # 输出True print(Fraction(3, 6) Fraction(1, 2)) # 输出True (自动化简后相等)与其他数值类型的混合运算Fraction可以与整数(int)、浮点数(float)、Decimal进行混合运算。但需要记住一个重要规则运算结果的类型由操作数中“精度更高”或“更特殊”的类型决定。Fraction与int运算结果为Fraction。Fraction与float运算结果为float。注意一旦与浮点数混合就会失去精确性重回浮点数的精度陷阱。Fraction与Decimal运算结果为Decimal。print(Fraction(1, 2) 1) # 输出3/2 (Fraction) print(Fraction(1, 2) 0.5) # 输出1.0 (float) print(Fraction(1, 2) Decimal(0.5)) # 输出1.0 (Decimal)类型转换你可以将Fraction转换为其他数字类型但转换可能损失信息。float(f): 转换为浮点数。这是有精度损失的转换。int(f): 转换为整数。这会直接截断小数部分相当于向下取整。int(Fraction(7, 2))结果是3。f.__floor__()和f.__ceil__(): 可以使用math.floor(f)和math.ceil(f)进行向下和向上取整。一个实用的方法as_integer_ratio()这个方法返回一个元组(分子, 分母)即分数化简后的分子和分母。它在需要将分数传递给某些只接受分子分母参数的API时非常有用。f Fraction(2.5).limit_denominator() # 先获取一个近似分数 5/2 ratio f.as_integer_ratio() print(ratio) # 输出(5, 2)3. 实战应用从成绩分析到复杂计算理解了基本操作我们来看几个实战案例把Fraction用活。3.1 案例一寻找“谁考得最好”——精确分数比较假设我们有一组学生的成绩但成绩是以分数形式记录的比如在某些竞赛或特定评分体系中。我们的任务是找出最高分。如果直接用字符串比较“1/2”和“2/3”会很麻烦而用浮点数转换又可能因精度问题在极端情况下出错虽然概率低。Fraction是完美选择。场景模拟from fractions import Fraction # 模拟学生成绩数据学生姓名 - 分数字符串形式 student_scores { “张三”: “18/20”, “李四”: “22/25”, “王五”: “85/100”, “赵六”: “13/15” } # 将分数字符串转换为Fraction对象并计算其数值浮点数仅用于展示 score_fractions {name: Fraction(score) for name, score in student_scores.items()} print(“分数对象”, score_fractions) # 输出{‘张三’: Fraction(9, 10), ‘李四’: Fraction(22, 25), ‘王五’: Fraction(17, 20), ‘赵六’: Fraction(13, 15)} # 方法1直接使用max函数Fraction支持比较 best_student max(score_fractions, keyscore_fractions.get) print(f“分数最高的学生是{best_student} 成绩为{student_scores[best_student]}”) # 输出分数最高的学生是赵六 成绩为13/15 # 方法2如果想看到具体数值和排名 sorted_scores sorted(score_fractions.items(), keylambda item: item[1], reverseTrue) print(“\n成绩排名”) for rank, (name, frac) in enumerate(sorted_scores, start1): print(f“第{rank}名{name} 分数{frac} ({float(frac):.2%})”) # 转换为百分制显示输出结果分数最高的学生是赵六 成绩为13/15 成绩排名 第1名赵六 分数13/15 (86.67%) 第2名张三 分数9/10 (90.00%) 第3名李四 分数22/25 (88.00%) 第4名王五 分数17/20 (85.00%)注意这里出现了一个有趣的现象。按分数值排名赵六13/15 ≈ 0.8667并不是数值最高的张三9/100.9才是。我的示例代码中max函数返回赵六是因为我在创建score_fractions字典时max函数遍历的顺序可能受Python 3.7字典插入顺序影响而keyscore_fractions.get在分数相等时不实际上13/15并不等于9/10。让我们检查一下Fraction(13,15) Fraction(9,10)吗计算一下13/15 ≈ 0.8666, 9/100.9。所以0.8666 0.9赵六的分数应该比张三低。那么为什么max会返回赵六呢问题出在max的key参数上。score_fractions.get返回的是Fraction对象它们是可以正确比较的。但如果字典中有多个最大值即分数相等max会返回第一个遇到的。然而在这个例子中分数并不相等。让我重新运行一下逻辑max(score_fractions, keyscore_fractions.get)这行代码会遍历字典的键‘张三’ ‘李四’ ‘王五’ ‘赵六’对每个键通过score_fractions.get(key)获取其值Fraction对象然后比较这些值。Fraction(9,10)是0.9Fraction(22,25)是0.88Fraction(17,20)是0.85Fraction(13,15)是0.8666…。最大值显然是0.9对应的键是‘张三’。所以理论上应该输出张三。如果我之前的示例输出是赵六那可能是代码或执行环境有误。正确的输出应该是张三。这个细节恰恰说明了测试和验证的重要性。在实际编码中对于关键逻辑一定要用明确的断言或打印中间结果来验证。修正后的结论Fraction能确保比较的绝对精确是进行此类排序、找最值操作的可靠基础。避坑技巧数据清洗实际数据可能包含“91/100”、“0.91”或“91%”等多种格式。在转换前需要统一的预处理函数。处理异常使用try...except包裹Fraction()转换过程以捕获无效格式如分母为0、非数字字符等。性能考虑如果处理成千上万条记录频繁创建Fraction对象会有开销。对于纯查找最大值的场景可以在循环中直接比较数值避免存储所有Fraction对象。3.2 案例二构建分数计算器我们来构建一个更复杂的、交互式的命令行分数计算器它能处理表达式如“1/2 3/4 * 2”。核心思路使用Python的eval()函数是危险的因为它会执行任意代码。更安全的做法是解析表达式。但为了演示Fraction的集成我们可以做一个简化版解析由数字、分数和运算符 -, *, /组成的字符串但数字和分数需要用空格隔开这是一种简单的词法约定。更实用的方法利用Fraction本身能解析字符串的特性我们可以设计一个支持两种输入模式的计算器单次运算计算 1/2 1/3连续运算用户输入分数和运算符逐步给出结果。这里我们实现一个简单的单次表达式求值版本使用eval()但通过限制命名空间来增强安全性。from fractions import Fraction import operator def safe_fraction_eval(expr: str): “”“安全地评估分数表达式。 表达式允许包含整数、Fraction()构造函数、以及 - * / 运算符和括号。 例如’Fraction(1,2) Fraction(3,4) * 2’ ”“” # 定义安全的命名空间只允许必要的函数和类型 safe_dict { ‘Fraction’: Fraction, ‘__builtins__‘: None, # 禁用内置函数极大提升安全性 } # 添加算术运算符虽然eval本身就能理解但显式定义更清晰 safe_dict.update({‘add’: operator.add, ‘sub’: operator.sub, ‘mul’: operator.mul, ‘truediv’: operator.truediv}) try: # 警告即使限制了命名空间eval仍有风险。此处仅用于教学演示。 # 生产环境应使用真正的语法解析器如ast模块。 result eval(expr, {“__builtins__“: None}, safe_dict) # 确保结果是Fraction如果是整数则转换 if isinstance(result, (int, float)): # 如果与整数运算结果可能是int与float运算结果可能是float。 # 我们尝试将int和有限的float转回Fraction if isinstance(result, int): return Fraction(result) else: # 对于float使用limit_denominator得到一个近似分数 return Fraction(result).limit_denominator(1000000) return result except ZeroDivisionError: return “错误除数不能为零” except Exception as e: return f“表达式解析错误{e}” # 测试这个计算器 if __name__ “__main__”: test_expressions [ “Fraction(1, 2) Fraction(1, 3)”, # 5/6 “Fraction(3, 4) * 2”, # 3/2 “1 Fraction(1, 2)”, # 3/2 (整数与分数运算) “Fraction(5, 6) / Fraction(2, 3)”, # 5/4 “Fraction(1, 2) 0.25”, # 0.75 (float) ] for expr in test_expressions: res safe_fraction_eval(expr) print(f“{expr} {res} (类型{type(res).__name__})”)输出示例Fraction(1, 2) Fraction(1, 3) 5/6 (类型Fraction) Fraction(3, 4) * 2 3/2 (类型Fraction) 1 Fraction(1, 2) 3/2 (类型Fraction) Fraction(5, 6) / Fraction(2, 3) 5/4 (类型Fraction) Fraction(1, 2) 0.25 0.75 (类型float)重要警告上述代码使用eval()是为了演示简便在生产环境中直接使用eval()处理用户输入是极其危险的行为会导致代码注入漏洞。一个更安全的方案是使用Python的ast抽象语法树模块解析表达式只允许特定的语法节点或者自己编写一个简单的语法解析器。对于学习目的可以限制表达式只能包含数字、运算符、括号和Fraction关键字并在沙盒环境中执行。3.3 案例三解决“洗衣机模糊推理”中的比例问题“洗衣机模糊推理”是一个经典的模糊逻辑控制案例用于根据衣物的污渍程度和油脂程度自动决定洗涤时间。虽然其核心是模糊集合和规则推理但在去模糊化将模糊输出结果转换为具体洗涤时间的步骤中经常需要计算不同隶属度下的加权平均这本质上是分数运算。假设我们有两条规则输出规则1洗涤时间“短”置信度或隶属度为 0.7规则2洗涤时间“长”置信度或隶属度为 0.3“短”时间我们定义为30分钟“长”时间定义为60分钟。最终洗涤时间可以通过加权平均计算总时间 (0.7 * 30 0.3 * 60) / (0.7 0.3) (21 18) / 1 39 分钟这里权重0.7和0.3的和为1计算很简单。但在更复杂的模糊系统中权重之和可能不为1或者权重本身是来自多个模糊规则合成的复杂分数。使用Fraction可以确保在合成权重和计算加权平均时避免浮点累加误差。模拟场景假设我们通过复杂的隶属函数计算得到三个洗涤时间建议及其精确的权重分数而非浮点数from fractions import Fraction from decimal import Decimal # 假设的规则输出 (洗涤时间分钟 权重分数) # 权重来自隶属度函数的精确计算这里用分数模拟 rules [ (30, Fraction(7, 10)), # 短时间权重7/10 (45, Fraction(1, 5)), # 中时间权重1/5 (60, Fraction(1, 10)), # 长时间权重1/10 ] # 验证权重和是否为1 total_weight sum(weight for _, weight in rules) print(f“总权重: {total_weight}”) # 输出 1 # 精确计算加权平均洗涤时间 # 加权平均 Σ(时间 * 权重) / Σ(权重) numerator sum(time * weight for time, weight in rules) weighted_avg numerator / total_weight # 因为total_weight是Fraction 结果也是Fraction print(f“加权平均洗涤时间分数: {weighted_avg} 分钟”) print(f“加权平均洗涤时间小数: {float(weighted_avg):.2f} 分钟”) # 对比如果使用浮点数计算理论上应该一致但展示了Fraction的精确性 float_rules [(30, 0.7), (45, 0.2), (60, 0.1)] float_avg sum(t * w for t, w in float_rules) / sum(w for _, w in float_rules) print(f“浮点数计算平均时间: {float_avg:.2f} 分钟”)输出总权重: 1 加权平均洗涤时间分数: 39/1 分钟 加权平均洗涤时间小数: 39.00 分钟 浮点数计算平均时间: 39.00 分钟在这个简单例子中两者结果相同。但如果权重是更复杂的分数如1/3, 1/7等且经过多次运算后Fraction的精确性优势就会体现出来能保证控制逻辑的确定性这对于需要高可靠性的嵌入式或控制系统尤为重要。4. 深入原理与性能考量4.1 Fraction的内部实现与自动化约分Fraction类在内部使用Python的整数int来存储分子和分母。其核心魔法在于自动化简。每当创建一个新的Fraction对象或进行运算产生新对象时它都会调用gcd函数最大公约数来化简分数。import math from fractions import Fraction # 查看化简过程 f Fraction(10, 15) print(f) # 输出 2/3 # 内部大致发生了 gcd math.gcd(10, 15) 5 # numerator 10 // 5 2 # denominator 15 // 5 3化简保证了分数的唯一表示这也是分数能够正确进行相等比较的基础。Fraction(2, 4)和Fraction(1, 2)是相等的因为它们化简后是同一个分数。自动化简的代价每次运算都计算gcd会带来额外的计算开销。对于性能极其敏感、且涉及大量分数运算的场景例如模拟分子动力学这可能会成为瓶颈。在这种情况下开发者有时会选择自己实现一个不自动约分的分数类在最后需要时才进行化简或者直接使用定点数、高精度小数Decimal来替代。4.2 与Decimal和float的对比与选型Python中处理“非整数”主要有三种类型float,Decimal,Fraction。如何选择特性float(浮点数)Decimal(十进制浮点数)Fraction(分数)本质二进制浮点数 (IEEE 754)十进制浮点数有理数分子/分母精度相对精度约15-17位有效数字可自定义精度默认28位绝对精确适用场景科学计算、图形、机器学习等性能要求高、能容忍微小误差的场景金融、货币计算、需要精确十进制表示的场景需要精确比例关系的场景如分数运算、概率、音乐、部分物理模拟性能最快硬件直接支持较慢软件模拟慢涉及整数运算和化简内存占用小通常8字节大取决于精度较大存储两个任意大整数典型问题0.1 0.2 ! 0.3无十进制表示误差但仍有精度限制分母可能变得非常大选型指南追求速度和通用性能接受微小误差用float。这是默认选择。处理金钱、需要精确的十进制小数用Decimal。这是金融领域的标准。处理分数、比例、需要绝对精确的有理数运算用Fraction。比如你要表示“三分之一加三分之一等于三分之二”而不是0.333… 0.333… ≈ 0.666…。一个关键技巧桥接转换有时你需要混合使用。记住转换路径Fraction-Decimal: 非常安全且精确因为Decimal可以精确表示分数转换来的小数如果分母是2和5的幂或者以高精度近似。Decimal(f)或Decimal(str(f))。Fraction-float: 有精度损失但通常可接受。float(f)。Decimal-Fraction: 安全且精确。Fraction(d)。float-Fraction:危险会捕获浮点数的二进制近似值。几乎总是应该先转字符串Fraction(str(flt))或使用limit_denominator()。4.3 常见陷阱与最佳实践在实际使用Fraction时我总结出以下几个需要特别注意的点陷阱1从浮点数构造这是最大的坑前面已强调多次。重申解决方案绝对不要Fraction(0.1)应该用Fraction(0.1)或Fraction(str(0.1))如果只能拿到浮点数变量且想得到一个“好看”的近似分数用Fraction(flt_var).limit_denominator(max_denominator1000)陷阱2分母过大导致性能下降或溢出在进行连续乘除运算时分子和分母可能会快速增长得非常巨大。# 连续乘法可能导致分母急剧膨胀 f Fraction(1, 1) for i in range(1, 10): f * Fraction(1, i) print(f) # 分母是 9! 362880虽然Python的整数可以处理任意大的数只要内存够但计算速度会变慢。如果程序变慢可以检查是否产生了巨大的分数。适时使用.limit_denominator()可以控制分母大小用微小的精度损失换取巨大的性能提升。陷阱3与float混合运算失去精确性一旦你的表达式里混入了float结果就会变成float精确性就此丧失。result Fraction(1, 3) 0.1 # result 是 float 类型约等于 0.43333333333333335最佳实践在计算开始前将所有操作数统一转换为Fraction或Decimal。陷阱4忽略ZeroDivisionError创建分数时分母为零会直接抛出ZeroDivisionError。在处理外部输入时务必做好异常处理。def safe_create_fraction(num_str, denom_str): try: num int(num_str) denom int(denom_str) return Fraction(num, denom) except (ValueError, ZeroDivisionError) as e: print(f“无效输入{e}”) return None最佳实践总结输入字符串化尽可能从字符串创建Fraction。类型统一在运算链中保持类型一致避免与float无意混合。适时化简关注分母大小对中间结果使用limit_denominator()进行控制。异常处理对用户输入或不可靠数据源进行包装。明确需求问自己我真的需要绝对精确的分数吗还是高精度的Decimal或快速的float就足够了5. 扩展应用与趣味编程掌握了基础我们可以玩点更花的。Fraction模块还能在一些意想不到的地方派上用场。5.1 音乐节奏与时长计算在音乐编程中音符的时长常常用分数表示。比如四分音符是1拍八分音符是1/2拍附点四分音符四分音符加附点是 1 1/2 3/2 拍。from fractions import Fraction # 定义基本音符时长 whole_note Fraction(4, 1) # 全音符4拍 half_note Fraction(2, 1) # 二分音符2拍 quarter_note Fraction(1, 1) # 四分音符1拍 (基准) eighth_note Fraction(1, 2) # 八分音符1/2拍 sixteenth_note Fraction(1, 4) # 十六分音符1/4拍 # 计算一个小节的时长假设4/4拍 measure_4_4 quarter_note * 4 print(f“一个4/4小节的总拍数{measure_4_4}”) # 计算一个节奏型 前八后十六 两个四分音符 rhythm_pattern eighth_note sixteenth_note sixteenth_note quarter_note quarter_note print(f“节奏型总时长{rhythm_pattern} 拍”) # 输出 2/1 拍即2拍 # 判断节奏型是否填满一个小节 if rhythm_pattern measure_4_4: print(“节奏型正好填满一个小节”) else: print(f“节奏型时长不足还差 {measure_4_4 - rhythm_pattern} 拍”)5.2 概率计算的精确表达在理论概率论或离散数学的教学、验证中使用Fraction可以精确计算概率避免浮点数误差带来的困惑。from fractions import Fraction from itertools import combinations # 经典问题从一副52张扑克牌中随机抽取5张得到同花顺的概率是多少 # 同花顺种类数 4种花色 * (13-4)种顺子起点 4 * 10 40 # 总组合数 C(52, 5) total_combinations Fraction(1, 1) # 用Fraction初始化 # 计算组合数 C(52,5) 52! / (5! * 47!) # 我们可以直接计算但更简单的是利用Fraction的自动化简 def comb(n, k): “”“计算组合数 C(n, k) 返回Fraction”“” if k n: return Fraction(0, 1) # 利用公式 C(n,k) n! / (k! * (n-k)!) # 直接连乘并相除Fraction会处理化简 result Fraction(1, 1) for i in range(1, k1): result * Fraction(n - k i, i) return result total comb(52, 5) straight_flush_count 40 probability Fraction(straight_flush_count, 1) / total print(f“总组合数{total}”) print(f“同花顺组合数{straight_flush_count}”) print(f“抽到同花顺的精确概率{probability}”) print(f“抽到同花顺的近似概率{float(probability):.10f}”)这样计算出的概率是精确的分数对于教学或需要精确比较概率大小的场景非常有价值。5.3 自定义分数运算与扩展Fraction类本身功能已经很强但你可以通过继承它来添加自定义功能。例如创建一个能输出带分数假分数化为整数和真分数部分的分数类。from fractions import Fraction class MixedFraction(Fraction): “”“扩展Fraction支持带分数表示”“” def as_mixed_number(self): “”“返回整数部分 真分数部分的元组”“” whole self.numerator // self.denominator remainder self.numerator % self.denominator if remainder 0: return (whole, Fraction(0, 1)) else: return (whole, Fraction(remainder, self.denominator)) def __str__(self): whole, frac self.as_mixed_number() if whole 0: return super().__str__() # 直接返回真分数表示 elif frac 0: return str(whole) # 返回整数 else: return f“{whole} {frac.numerator}/{frac.denominator}” # 使用示例 f1 MixedFraction(7, 3) # 7/3 2 1/3 f2 MixedFraction(4, 2) # 4/2 2 f3 MixedFraction(1, 4) # 1/4 print(f“f1 {f1}”) # 输出 f1 2 1/3 print(f“f2 {f2}”) # 输出 f2 2 print(f“f3 {f3}”) # 输出 f3 1/4 # 仍然支持所有Fraction的运算 f4 f1 f3 print(f“f1 f3 {f4}”) # 输出 f1 f3 2 7/12 (即 31/12) print(f“f4的内部值{f4.numerator}/{f4.denominator}”) # 输出 31/12通过继承我们既保留了Fraction所有强大的运算能力又增加了适合特定场景如小学数学教学的表示功能。
返回列表