(首先是的,我确实知道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是否有类似扰流标签的东西?你知道,在里面写东西但没有显示)
答案 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类型添加类型分数未实现”这样更直观的东西。