Python:良好实践:类:模拟分数

时间:2014-03-07 20:19:00

标签: python class fractions

(首先是的,我确实知道Fraction模块存在,但我正在自己练习!) 我的问题是,有这个代码:

class Fraction(object):
    def __init__(self,num,den=1,reduce=True):
        # only accept integers
        if not(type(num) == int and type(den) == int):
            raise RuntimeError("You must pass integers as numerator \
and denominator!")
        # don't accept fractions with denominator 0
        if den == 0:
            raise ZeroDivisionError("The denominator must not be 0")
        # if both num and den are negative, flip both
        if num < 0 and den < 0:
            num = abs(num)
            den = abs(num)
        # if only the den is negative, change the "-" to the numerator
        elif den < 0:
            num *= -1
            den = abs(den)
        self.num = num
        self.den = den
        # the self.auto is a variable that will tell us if we are supposed to
        #automatically reduce the Fraction to its lower terms. when doing some
        #maths, if either one of the fractions has self.auto==False, the result
        #will also have self.auto==False
        self.auto = reduce
        if self.auto:
            self.reduce()

    def reduce(self):
        '''used to reduce the fraction to its lower terms using Euclid's\
Algorith'''
        a = self.num
        b = self.den
        # With the Euclid's Algorithm, the GCD of A and B, is B if B divides
        #evenly A. Otherwise, make B the new A, and make B the remainder of A/B!
        #e.g.(30,20). 30/20 is not integer... (30-20=10)
        # 20/10 = integer! 10 is the GCD of 20 and 30
        while a%b != 0:
            olda = a
            oldb = b
            a = oldb
            b = olda%oldb
        self.num //= b
        self.den //= b

    def __add__(self,other):
        '''addition implementation'''
        if type(other) == int:
            other = Fraction(other,1)
        elif type(other) == float:
            return NotImplemented
        num = self.num*other.den + self.den*other.num
        den = self.den * other.den
        return Fraction(num,den,self.auto and other.auto)

    def __radd__(self,other):
        '''raddition implemented enables integer + fraction statements'''
        # other is always a Fraction!! Well, we are in R(ight)__add__, so this
        #was called because the thingy on the right IS a Fraction
        # In this case we have to manipulate the "self" argument
        if type(self) == int:
            self = Fraction(self,1)
        elif type(self) == float:
            return NotImplemented
        num = self.num*other.den + self.den*other.num
        den = self.den * other.den
        return Fraction(num,den,self.auto and other.auto)

你认为在实现__radd__方法时,只需调用__add__反转参数是好的做法吗? 或者更好的做法就是这样做? (我假设__add____radd__的答案适用于所有其他数学函数......) 我已经有很多代码,但为了简洁起见,我只是这个就够了...... (另外,有人能告诉我stackoverflow是否有类似扰流标签的东西?你知道,在里面写东西但没有显示)

2 个答案:

答案 0 :(得分:2)

请注意,当您执行1 + myFraction时,Python首先尝试执行int.__add__(1, myFraction),但由于未实现,因此调用Fraction.__radd__(myFraction, 1),因此使用反向参数。

我想最佳做法是,如果您关心可维护代码,请在__radd__中规范化参数,然后在转换后的参数上调用__add__。通过这种方式,您do not duplicate需要执行添加所需的代码。像这样:

def __radd__(self, other):
    if type(other) == int:
        other = Fraction(other, 1)
    elif type(other) == some_other_format_I_can_convert_to_Fraction:
        other = some_conversion(other)
    else:
        raise NotImplemented
    return self.__add__(other)

您甚至可以跳转到Fraction并将other直接传递给__add__,因为该功能已经知道要进行转换。如果你关心速度而不是代码美,你可以直接在__radd__进行计算。但在这种情况下,添加的数学将会重复,所以如果你发现错误,你必须记住在2个地方修复它。

答案 1 :(得分:2)

一般来说,编写两段执行相同操作的代码是不好的做法。如果两个操作,在这种情况下,当self在左边时添加到另一个对象并在self在右边时添加到另一个对象,最终执行相同的操作,最好只需__radd__调用{{ 1}}并返回结果。这样,如果您修改了分数添加的行为,例如,如果您想添加使用浮点数的功能,则只需要在一个地方而不是两个地方修改代码。

请注意,您不需要反转参数;无论您使用__add__还是__radd____add__始终是第一个参数。在文档中,示例给出self,其中x不实现减法。调用是x - y,所以自我永远是你的对象。所以你仍然需要检查其他的类型,因为self总是一个分数。 (见:Documentation for __radd__

我的实现,假设所有其他相同的将是:

y.__rsub__(x)

编辑: 请注意,这仅在操作数顺序无关紧要时才有效。对于减法,您必须先将其他值转换为分数,然后在其上调用 def __add__(self, other): # however you want to implement __add__ def __radd__(self, other): return self.__add__(other)

__sub__

此外,为了安全地使用所有类型,如果类型不是float或int,您可能需要 def __sub__(self, other): # however you implement __sub__ def __rsub__(self, other): if type(other) != Fraction: if type(other) == int: other = Fraction(other, 1) else: return NotImplemented return other.__sub__(self) 或引发错误。现在,您的类与其他对象类型的行为是未定义的。例如,如果我有一个名为num和den的属性的对象,但是我的是分别是一个浮点数和一个字符串,你的代码给我的错误是“你必须从构造函数中传递整数作为分子和分母”,而不是而不是像“使用MyType类型添加类型分数未实现”这样更直观的东西。