【问题标题】:Python : Good practices : Classes : Emulating fractionsPython:良好实践:类:模拟分数
【发布时间】:2014-03-07 20:18:59
【问题描述】:

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

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是否有像剧透标签之类的东西?你知道,在里面写东西但不显示)

【问题讨论】:

    标签: python class fractions


    【解决方案1】:

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

    请注意,您不需要反转参数;无论您使用__radd__ 还是__add__self 始终是第一个参数。在文档中,示例给出了x - y,其中 x 不实现减法。拨打电话是y.__rsub__(x),因此 self 将永远是您的对象。所以你仍然需要检查 other 的类型,因为 self 永远是一个分数。 (见:Documentation for __radd__

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

        def __add__(self, other):
            # however you want to implement __add__
    
        def __radd__(self, other):
            return self.__add__(other)
    

    编辑: 请注意,这仅在操作数顺序无关紧要时才有效。对于减法,您必须先将 other 转换为分数,然后再调用 __sub__

        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)
    

    此外,为了安全使用所有类型,您可能需要else: return NotImplemented 或如果类型不是浮点或整数则引发错误。现在你的类对其他对象类型的行为是未定义的。例如,如果我有一个具有名为 num 和 den 的属性的对象,但我的对象分别是浮点数和字符串,那么您的代码会给我的错误是“您必须将整数作为分子和分母传递”,来自构造函数,而不是比“未实现使用 MyType 类型添加类型 Fraction”等更直观的东西。

    【讨论】:

      【解决方案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__ 中进行计算。但是在这种情况下,做加法的数学运算会重复,所以如果你发现一个错误,你必须记住在两个地方修复它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-26
        • 1970-01-01
        • 2011-02-05
        • 1970-01-01
        • 1970-01-01
        • 2015-04-01
        相关资源
        最近更新 更多