python中的Fraction类

Fraction类(对于分数的精确计算)

方法定义:所有类都应该首先提供构造方法,构造方法定义了数据对象的创建方式,构造方法总是命名为__init__

class Fraction:
    def __init__(self,top,bottom):
        self.num=top
        self.den=bottom

self是一个总是指向对象本身的特殊参数,必须是第一个形式参数,然而在调用方法不需要提供相应的实际参数,分数需要分子和分母两部分状态数据,self.num是分子,self.den是分母

myfraction=Fraction(3,5)

以上代码创建了一个对象,名为myfraction,值为3/5

print(myfraction)
打印结果:
 <__main__.Fraction object at 0x000001DA96D3A400>

Fraction对象并不知道如何响应打印请求。
print函数要求对象将自己转换成一个可以被写到输出端的字符串,myfraction唯一能做的就是显示存储变量中的实际引用(地址本身),这并不是我们想要的结果。

有两种办法可以解决这个问题,一种是定义一个show方法,使得Fraction对象能将自己作为字符串来打印。

    def show(self):
        print(self.num,"/",self.den)

可以实现打印Fraction方法:

>>> myf=Fraction(3,5)
>>> myf.show()
3/5
>>> print(myf)
<__main__.Fraction object at 0x0000025726E6DE20>

python所有的类都提供了一套标准方法,但是可能没有正常工作。
其中之一就是将对象转换成字符串的方法__str__,这个方法的默认实现是像我们之前所见的那样返回实例的地址字符串。
我们需要做的是为这个方法提供一个“更好”的实现,即重写默认实现,或者说重新定义该方法的行为。

    def __str__(self):
        return str(self.num)+"/"+str(self.den)

除了self之外,该方法定义不需要其他信息,新的方法通过将两部分内部状态数据转换成字符串并在之间插入“/”来进行转换

>>> myf=Fraction(3,5)
>>> print(myf)
3/5
>>> print("I ate",myf,"of the pizza")
I ate 3/5 of the pizza
>>> myf.__str__()
'3/5'
>>> str(myf)
'3/5'
>>> 

可以重写Fraction类中的很多其他方法,最重要的是一些基本的数学运算。

我们想创建两个Fraction对象,然后将他们相加。目前会得到如下结果:

>>> f1=Fraction(1,4)
>>> f2=Fraction(1,2)
>>> f1+f2
Traceback (most recent call last):
  File "", line 1, in <module>
    f1+f2
TypeError: unsupported operand type(s) for +: 'Fraction' and 'Fraction'

这个错误表示,+号无法处理Fraction的操作数。
可以通过重写Fraction类的__add__方法来修正这个错误。该方法需要两个参数。
第一个仍然是self,第二个代表了表达式中的另一个操作数。
两个分数需要有相同分母才能相加。

    def __add__(self, otherfraction):
    
        newnum = self.num * otherfraction.den + \
            self.den * otherfraction.num
        newden = self.den * otherfraction.den
        
        return Fraction(newnum,newden)

注意: \是续行符

>>>f1=Fraction(1,4)
>>>f2=Fraction(1,2)
>>>f3=f1+f2
>>>print(f3)
6/8

结果虽然正确,但并不是最简分数,最好的表达应该是3/4

为了保证结果是最简分数,需要一个知道如何化简分数的辅助方法,该方法需要寻找分子和分母的最大公因数,然后将分子和分母分别除以最大公因数,最后的结果就是最简分数。

最著名的方法就是欧几里得算法,用迭代实现。对于整数m和n,如果m能被n整除,那么它们的最大公因数就是n,如果不能,那么结果是n与m除以n的余数的最大公因数。

def gcd(m,n):
    while m%n != 0:
        oldm=m
        oldn=n
        
        m=oldn
        n=oldm%oldn
    return n

现在可以利用这个函数来化简分数。
改良版__add__方法如下:

    def __add__(self, otherfraction):
        newnum = self.num * otherfraction.den + \
                 self.den * otherfraction.num
        newden = self.den * otherfraction.den
        common = gcd(newnum,newden)
        return Fraction(newnum//common,newden//common)

结果实现化简:

>>>f1=Fraction(1,4)
>>>f2=Fraction(1,2)
>>>f3=f1+f2
>>>print(f3)
3/4

为了允许两个分数互相比较,还需要添加一些方法。
假设有两个Fraction对象f1和f2,只有在它们是同一个对象的引用时,f1==f2才为True,这被称为浅相等。在当前实现中,分子和分母相同的两个不同对象时不相等的。

通过重写__eq__方法,可以建立深相等——根据值来判断,而不是根据引用。
__eq__是又一个在任意类中都有的标准方法,它比较两个对象,并且在它们的值相等的时候返回True,否则返回False

    def __eq__(self, other):
        firstnum = self.num * other.den
        secondnum = other.num * self.den
        
        return firstnum == secondnum

通过统一两个分数的分母来比较分子

剩余算数方法都可以重写以实现新的功能,在此不一一举例。
到目前为止Fraction类的完整实现如下:

class Fraction:
    def __init__(self,top,bottom):
        self.num=top
        self.den=bottom
    
    def __str__(self):
        return str(self.num)+"/"+str(self.den)
    
    def show(self):
        print(self.num,"/",self.den)
    
    def __add__(self, otherfraction):
        newnum = self.num * otherfraction.den + \
                 self.den * otherfraction.num
        newden = self.den * otherfraction.den
        common = gcd(newnum,newden)
        return Fraction(newnum//common,newden//common)
    
    def __eq__(self, other):
        firstnum = self.num * other.den
        secondnum = other.num * self.den
        return firstnum == secondnum

你可能感兴趣的:(python)