
本文首发于微信公众号「拙见python」,基础板块第10 篇。
适合人群:已阅读第 2 篇:Python 面向对象编程并动手敲过代码的读者。
题目难度:⭐ → ⭐⭐⭐⭐⭐ 逐步提升,建议先自己写,再看答案。
考察知识点:魔术方法(add、sub、mul、eq、lt、str、repr、float)
定义一个
Fraction(分数)类:
初始化接收 numerator(分子)和denominator(分母),分母不能为 0自动约分(使用 math.gcd)支持 +、-、*运算,返回新的Fraction对象支持 ==和<比较print()输出"3/4"格式,repr()输出"Fraction(3, 4)"格式支持 float()转换为小数测试:
1/2 + 1/3 = 5/6,2/3 * 3/4 = 1/2
参考答案:
import mathclass Fraction:def __init__(self, numerator, denominator):if denominator == 0:raise ValueError(”分母不能为 0”)# 处理负号:统一放到分子上if denominator < 0:numerator = -numeratordenominator = -denominator# 约分gcd = math.gcd(abs(numerator), abs(denominator))self.numerator = numerator // gcdself.denominator = denominator // gcd# -------- 运算符重载 --------def __add__(self, other):# a/b + c/d = (a*d + c*b) / (b*d)new_num = self.numerator * other.denominator + other.numerator * self.denominatornew_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)def __sub__(self, other):new_num = self.numerator * other.denominator - other.numerator * self.denominatornew_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)def __mul__(self, other):new_num = self.numerator * other.numeratornew_den = self.denominator * other.denominatorreturn Fraction(new_num, new_den)# -------- 比较运算 --------def __eq__(self, other):return self.numerator == other.numerator and self.denominator == other.denominatordef __lt__(self, other):# a/b < c/d ⟺ a*d < c*b(分母已保证为正)return self.numerator * other.denominator < other.numerator * self.denominator# -------- 字符串表示 --------def __str__(self):if self.denominator == 1:return str(self.numerator)# 4/1 → ”4”return f”{self.numerator}/{self.denominator}”def __repr__(self):return f”Fraction({self.numerator}, {self.denominator})”# -------- 类型转换 --------def __float__(self):return self.numerator / self.denominator# 测试f1 = Fraction(1, 2)f2 = Fraction(1, 3)f3 = Fraction(2, 3)f4 = Fraction(3, 4)# 运算print(f”{f1} + {f2} = {f1 + f2}”)# 1/2 + 1/3 = 5/6print(f”{f3} * {f4} = {f3 * f4}”)# 2/3 * 3/4 = 1/2(自动约分)print(f”{f1} - {f2} = {f1 - f2}”)# 1/2 - 1/3 = 1/6# 比较print(f”{f1} == {Fraction(2, 4)}: {f1 == Fraction(2, 4)}”)# True(2/4 约分后等于 1/2)print(f”{f2} < {f1}: {f2 < f1}”)# True(1/3 < 1/2)# 字符串与类型转换print(repr(f1))# Fraction(1, 2)print(f”float({f1}) = {float(f1)}”)# float(1/2) = 0.5print(f”float({f1 + f2}) = {float(f1 + f2):.4f}”)# float(5/6) = 0.8333# 整数分数的特殊显示print(Fraction(8, 2))# 4(分母为 1 时只显示分子)
思路解析:
约分:构造时用 math.gcd 求最大公约数,分子分母同除以 GCD。例如 Fraction(2, 4) 约分后变成 1/2。
负号处理:统一将负号放在分子上,如 Fraction(1, -2) 变成 Fraction(-1, 2),方便后续比较运算。
加法公式:a/b + c/d = (a*d + c*b) / (b*d),构造新的 Fraction 时会自动约分。
__str__ 面向用户(print),__repr__ 面向开发者(调试时看到对象状态)。
__float__ 让 float(fraction) 能正常工作,这是把自定义对象融入 Python 数字生态的关键。
建议的练习方式:
关注「拙见python」,一起写干净的代码。
下一篇预告:Python 面向对象习题之8/10