(root)/
Python-3.11.7/
Lib/
test/
test_binop.py
       1  """Tests for binary operators on subtypes of built-in types."""
       2  
       3  import unittest
       4  from operator import eq, le, ne
       5  from abc import ABCMeta
       6  
       7  def gcd(a, b):
       8      """Greatest common divisor using Euclid's algorithm."""
       9      while a:
      10          a, b = b%a, a
      11      return b
      12  
      13  def isint(x):
      14      """Test whether an object is an instance of int."""
      15      return isinstance(x, int)
      16  
      17  def isnum(x):
      18      """Test whether an object is an instance of a built-in numeric type."""
      19      for T in int, float, complex:
      20          if isinstance(x, T):
      21              return 1
      22      return 0
      23  
      24  def isRat(x):
      25      """Test whether an object is an instance of the Rat class."""
      26      return isinstance(x, Rat)
      27  
      28  class ESC[4;38;5;81mRat(ESC[4;38;5;149mobject):
      29  
      30      """Rational number implemented as a normalized pair of ints."""
      31  
      32      __slots__ = ['_Rat__num', '_Rat__den']
      33  
      34      def __init__(self, num=0, den=1):
      35          """Constructor: Rat([num[, den]]).
      36  
      37          The arguments must be ints, and default to (0, 1)."""
      38          if not isint(num):
      39              raise TypeError("Rat numerator must be int (%r)" % num)
      40          if not isint(den):
      41              raise TypeError("Rat denominator must be int (%r)" % den)
      42          # But the zero is always on
      43          if den == 0:
      44              raise ZeroDivisionError("zero denominator")
      45          g = gcd(den, num)
      46          self.__num = int(num//g)
      47          self.__den = int(den//g)
      48  
      49      def _get_num(self):
      50          """Accessor function for read-only 'num' attribute of Rat."""
      51          return self.__num
      52      num = property(_get_num, None)
      53  
      54      def _get_den(self):
      55          """Accessor function for read-only 'den' attribute of Rat."""
      56          return self.__den
      57      den = property(_get_den, None)
      58  
      59      def __repr__(self):
      60          """Convert a Rat to a string resembling a Rat constructor call."""
      61          return "Rat(%d, %d)" % (self.__num, self.__den)
      62  
      63      def __str__(self):
      64          """Convert a Rat to a string resembling a decimal numeric value."""
      65          return str(float(self))
      66  
      67      def __float__(self):
      68          """Convert a Rat to a float."""
      69          return self.__num*1.0/self.__den
      70  
      71      def __int__(self):
      72          """Convert a Rat to an int; self.den must be 1."""
      73          if self.__den == 1:
      74              try:
      75                  return int(self.__num)
      76              except OverflowError:
      77                  raise OverflowError("%s too large to convert to int" %
      78                                        repr(self))
      79          raise ValueError("can't convert %s to int" % repr(self))
      80  
      81      def __add__(self, other):
      82          """Add two Rats, or a Rat and a number."""
      83          if isint(other):
      84              other = Rat(other)
      85          if isRat(other):
      86              return Rat(self.__num*other.__den + other.__num*self.__den,
      87                         self.__den*other.__den)
      88          if isnum(other):
      89              return float(self) + other
      90          return NotImplemented
      91  
      92      __radd__ = __add__
      93  
      94      def __sub__(self, other):
      95          """Subtract two Rats, or a Rat and a number."""
      96          if isint(other):
      97              other = Rat(other)
      98          if isRat(other):
      99              return Rat(self.__num*other.__den - other.__num*self.__den,
     100                         self.__den*other.__den)
     101          if isnum(other):
     102              return float(self) - other
     103          return NotImplemented
     104  
     105      def __rsub__(self, other):
     106          """Subtract two Rats, or a Rat and a number (reversed args)."""
     107          if isint(other):
     108              other = Rat(other)
     109          if isRat(other):
     110              return Rat(other.__num*self.__den - self.__num*other.__den,
     111                         self.__den*other.__den)
     112          if isnum(other):
     113              return other - float(self)
     114          return NotImplemented
     115  
     116      def __mul__(self, other):
     117          """Multiply two Rats, or a Rat and a number."""
     118          if isRat(other):
     119              return Rat(self.__num*other.__num, self.__den*other.__den)
     120          if isint(other):
     121              return Rat(self.__num*other, self.__den)
     122          if isnum(other):
     123              return float(self)*other
     124          return NotImplemented
     125  
     126      __rmul__ = __mul__
     127  
     128      def __truediv__(self, other):
     129          """Divide two Rats, or a Rat and a number."""
     130          if isRat(other):
     131              return Rat(self.__num*other.__den, self.__den*other.__num)
     132          if isint(other):
     133              return Rat(self.__num, self.__den*other)
     134          if isnum(other):
     135              return float(self) / other
     136          return NotImplemented
     137  
     138      def __rtruediv__(self, other):
     139          """Divide two Rats, or a Rat and a number (reversed args)."""
     140          if isRat(other):
     141              return Rat(other.__num*self.__den, other.__den*self.__num)
     142          if isint(other):
     143              return Rat(other*self.__den, self.__num)
     144          if isnum(other):
     145              return other / float(self)
     146          return NotImplemented
     147  
     148      def __floordiv__(self, other):
     149          """Divide two Rats, returning the floored result."""
     150          if isint(other):
     151              other = Rat(other)
     152          elif not isRat(other):
     153              return NotImplemented
     154          x = self/other
     155          return x.__num // x.__den
     156  
     157      def __rfloordiv__(self, other):
     158          """Divide two Rats, returning the floored result (reversed args)."""
     159          x = other/self
     160          return x.__num // x.__den
     161  
     162      def __divmod__(self, other):
     163          """Divide two Rats, returning quotient and remainder."""
     164          if isint(other):
     165              other = Rat(other)
     166          elif not isRat(other):
     167              return NotImplemented
     168          x = self//other
     169          return (x, self - other * x)
     170  
     171      def __rdivmod__(self, other):
     172          """Divide two Rats, returning quotient and remainder (reversed args)."""
     173          if isint(other):
     174              other = Rat(other)
     175          elif not isRat(other):
     176              return NotImplemented
     177          return divmod(other, self)
     178  
     179      def __mod__(self, other):
     180          """Take one Rat modulo another."""
     181          return divmod(self, other)[1]
     182  
     183      def __rmod__(self, other):
     184          """Take one Rat modulo another (reversed args)."""
     185          return divmod(other, self)[1]
     186  
     187      def __eq__(self, other):
     188          """Compare two Rats for equality."""
     189          if isint(other):
     190              return self.__den == 1 and self.__num == other
     191          if isRat(other):
     192              return self.__num == other.__num and self.__den == other.__den
     193          if isnum(other):
     194              return float(self) == other
     195          return NotImplemented
     196  
     197  class ESC[4;38;5;81mRatTestCase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     198      """Unit tests for Rat class and its support utilities."""
     199  
     200      def test_gcd(self):
     201          self.assertEqual(gcd(10, 12), 2)
     202          self.assertEqual(gcd(10, 15), 5)
     203          self.assertEqual(gcd(10, 11), 1)
     204          self.assertEqual(gcd(100, 15), 5)
     205          self.assertEqual(gcd(-10, 2), -2)
     206          self.assertEqual(gcd(10, -2), 2)
     207          self.assertEqual(gcd(-10, -2), -2)
     208          for i in range(1, 20):
     209              for j in range(1, 20):
     210                  self.assertTrue(gcd(i, j) > 0)
     211                  self.assertTrue(gcd(-i, j) < 0)
     212                  self.assertTrue(gcd(i, -j) > 0)
     213                  self.assertTrue(gcd(-i, -j) < 0)
     214  
     215      def test_constructor(self):
     216          a = Rat(10, 15)
     217          self.assertEqual(a.num, 2)
     218          self.assertEqual(a.den, 3)
     219          a = Rat(10, -15)
     220          self.assertEqual(a.num, -2)
     221          self.assertEqual(a.den, 3)
     222          a = Rat(-10, 15)
     223          self.assertEqual(a.num, -2)
     224          self.assertEqual(a.den, 3)
     225          a = Rat(-10, -15)
     226          self.assertEqual(a.num, 2)
     227          self.assertEqual(a.den, 3)
     228          a = Rat(7)
     229          self.assertEqual(a.num, 7)
     230          self.assertEqual(a.den, 1)
     231          try:
     232              a = Rat(1, 0)
     233          except ZeroDivisionError:
     234              pass
     235          else:
     236              self.fail("Rat(1, 0) didn't raise ZeroDivisionError")
     237          for bad in "0", 0.0, 0j, (), [], {}, None, Rat, unittest:
     238              try:
     239                  a = Rat(bad)
     240              except TypeError:
     241                  pass
     242              else:
     243                  self.fail("Rat(%r) didn't raise TypeError" % bad)
     244              try:
     245                  a = Rat(1, bad)
     246              except TypeError:
     247                  pass
     248              else:
     249                  self.fail("Rat(1, %r) didn't raise TypeError" % bad)
     250  
     251      def test_add(self):
     252          self.assertEqual(Rat(2, 3) + Rat(1, 3), 1)
     253          self.assertEqual(Rat(2, 3) + 1, Rat(5, 3))
     254          self.assertEqual(1 + Rat(2, 3), Rat(5, 3))
     255          self.assertEqual(1.0 + Rat(1, 2), 1.5)
     256          self.assertEqual(Rat(1, 2) + 1.0, 1.5)
     257  
     258      def test_sub(self):
     259          self.assertEqual(Rat(7, 2) - Rat(7, 5), Rat(21, 10))
     260          self.assertEqual(Rat(7, 5) - 1, Rat(2, 5))
     261          self.assertEqual(1 - Rat(3, 5), Rat(2, 5))
     262          self.assertEqual(Rat(3, 2) - 1.0, 0.5)
     263          self.assertEqual(1.0 - Rat(1, 2), 0.5)
     264  
     265      def test_mul(self):
     266          self.assertEqual(Rat(2, 3) * Rat(5, 7), Rat(10, 21))
     267          self.assertEqual(Rat(10, 3) * 3, 10)
     268          self.assertEqual(3 * Rat(10, 3), 10)
     269          self.assertEqual(Rat(10, 5) * 0.5, 1.0)
     270          self.assertEqual(0.5 * Rat(10, 5), 1.0)
     271  
     272      def test_div(self):
     273          self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
     274          self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
     275          self.assertEqual(2 / Rat(5), Rat(2, 5))
     276          self.assertEqual(3.0 * Rat(1, 2), 1.5)
     277          self.assertEqual(Rat(1, 2) * 3.0, 1.5)
     278  
     279      def test_floordiv(self):
     280          self.assertEqual(Rat(10) // Rat(4), 2)
     281          self.assertEqual(Rat(10, 3) // Rat(4, 3), 2)
     282          self.assertEqual(Rat(10) // 4, 2)
     283          self.assertEqual(10 // Rat(4), 2)
     284  
     285      def test_eq(self):
     286          self.assertEqual(Rat(10), Rat(20, 2))
     287          self.assertEqual(Rat(10), 10)
     288          self.assertEqual(10, Rat(10))
     289          self.assertEqual(Rat(10), 10.0)
     290          self.assertEqual(10.0, Rat(10))
     291  
     292      def test_true_div(self):
     293          self.assertEqual(Rat(10, 3) / Rat(5, 7), Rat(14, 3))
     294          self.assertEqual(Rat(10, 3) / 3, Rat(10, 9))
     295          self.assertEqual(2 / Rat(5), Rat(2, 5))
     296          self.assertEqual(3.0 * Rat(1, 2), 1.5)
     297          self.assertEqual(Rat(1, 2) * 3.0, 1.5)
     298          self.assertEqual(eval('1/2'), 0.5)
     299  
     300      # XXX Ran out of steam; TO DO: divmod, div, future division
     301  
     302  
     303  class ESC[4;38;5;81mOperationLogger:
     304      """Base class for classes with operation logging."""
     305      def __init__(self, logger):
     306          self.logger = logger
     307      def log_operation(self, *args):
     308          self.logger(*args)
     309  
     310  def op_sequence(op, *classes):
     311      """Return the sequence of operations that results from applying
     312      the operation `op` to instances of the given classes."""
     313      log = []
     314      instances = []
     315      for c in classes:
     316          instances.append(c(log.append))
     317  
     318      try:
     319          op(*instances)
     320      except TypeError:
     321          pass
     322      return log
     323  
     324  class ESC[4;38;5;81mA(ESC[4;38;5;149mOperationLogger):
     325      def __eq__(self, other):
     326          self.log_operation('A.__eq__')
     327          return NotImplemented
     328      def __le__(self, other):
     329          self.log_operation('A.__le__')
     330          return NotImplemented
     331      def __ge__(self, other):
     332          self.log_operation('A.__ge__')
     333          return NotImplemented
     334  
     335  class ESC[4;38;5;81mB(ESC[4;38;5;149mOperationLogger, metaclass=ESC[4;38;5;149mABCMeta):
     336      def __eq__(self, other):
     337          self.log_operation('B.__eq__')
     338          return NotImplemented
     339      def __le__(self, other):
     340          self.log_operation('B.__le__')
     341          return NotImplemented
     342      def __ge__(self, other):
     343          self.log_operation('B.__ge__')
     344          return NotImplemented
     345  
     346  class ESC[4;38;5;81mC(ESC[4;38;5;149mB):
     347      def __eq__(self, other):
     348          self.log_operation('C.__eq__')
     349          return NotImplemented
     350      def __le__(self, other):
     351          self.log_operation('C.__le__')
     352          return NotImplemented
     353      def __ge__(self, other):
     354          self.log_operation('C.__ge__')
     355          return NotImplemented
     356  
     357  class ESC[4;38;5;81mV(ESC[4;38;5;149mOperationLogger):
     358      """Virtual subclass of B"""
     359      def __eq__(self, other):
     360          self.log_operation('V.__eq__')
     361          return NotImplemented
     362      def __le__(self, other):
     363          self.log_operation('V.__le__')
     364          return NotImplemented
     365      def __ge__(self, other):
     366          self.log_operation('V.__ge__')
     367          return NotImplemented
     368  B.register(V)
     369  
     370  
     371  class ESC[4;38;5;81mOperationOrderTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     372      def test_comparison_orders(self):
     373          self.assertEqual(op_sequence(eq, A, A), ['A.__eq__', 'A.__eq__'])
     374          self.assertEqual(op_sequence(eq, A, B), ['A.__eq__', 'B.__eq__'])
     375          self.assertEqual(op_sequence(eq, B, A), ['B.__eq__', 'A.__eq__'])
     376          # C is a subclass of B, so C.__eq__ is called first
     377          self.assertEqual(op_sequence(eq, B, C), ['C.__eq__', 'B.__eq__'])
     378          self.assertEqual(op_sequence(eq, C, B), ['C.__eq__', 'B.__eq__'])
     379  
     380          self.assertEqual(op_sequence(le, A, A), ['A.__le__', 'A.__ge__'])
     381          self.assertEqual(op_sequence(le, A, B), ['A.__le__', 'B.__ge__'])
     382          self.assertEqual(op_sequence(le, B, A), ['B.__le__', 'A.__ge__'])
     383          self.assertEqual(op_sequence(le, B, C), ['C.__ge__', 'B.__le__'])
     384          self.assertEqual(op_sequence(le, C, B), ['C.__le__', 'B.__ge__'])
     385  
     386          self.assertTrue(issubclass(V, B))
     387          self.assertEqual(op_sequence(eq, B, V), ['B.__eq__', 'V.__eq__'])
     388          self.assertEqual(op_sequence(le, B, V), ['B.__le__', 'V.__ge__'])
     389  
     390  class ESC[4;38;5;81mSupEq(ESC[4;38;5;149mobject):
     391      """Class that can test equality"""
     392      def __eq__(self, other):
     393          return True
     394  
     395  class ESC[4;38;5;81mS(ESC[4;38;5;149mSupEq):
     396      """Subclass of SupEq that should fail"""
     397      __eq__ = None
     398  
     399  class ESC[4;38;5;81mF(ESC[4;38;5;149mobject):
     400      """Independent class that should fall back"""
     401  
     402  class ESC[4;38;5;81mX(ESC[4;38;5;149mobject):
     403      """Independent class that should fail"""
     404      __eq__ = None
     405  
     406  class ESC[4;38;5;81mSN(ESC[4;38;5;149mSupEq):
     407      """Subclass of SupEq that can test equality, but not non-equality"""
     408      __ne__ = None
     409  
     410  class ESC[4;38;5;81mXN:
     411      """Independent class that can test equality, but not non-equality"""
     412      def __eq__(self, other):
     413          return True
     414      __ne__ = None
     415  
     416  class ESC[4;38;5;81mFallbackBlockingTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
     417      """Unit tests for None method blocking"""
     418  
     419      def test_fallback_rmethod_blocking(self):
     420          e, f, s, x = SupEq(), F(), S(), X()
     421          self.assertEqual(e, e)
     422          self.assertEqual(e, f)
     423          self.assertEqual(f, e)
     424          # left operand is checked first
     425          self.assertEqual(e, x)
     426          self.assertRaises(TypeError, eq, x, e)
     427          # S is a subclass, so it's always checked first
     428          self.assertRaises(TypeError, eq, e, s)
     429          self.assertRaises(TypeError, eq, s, e)
     430  
     431      def test_fallback_ne_blocking(self):
     432          e, sn, xn = SupEq(), SN(), XN()
     433          self.assertFalse(e != e)
     434          self.assertRaises(TypeError, ne, e, sn)
     435          self.assertRaises(TypeError, ne, sn, e)
     436          self.assertFalse(e != xn)
     437          self.assertRaises(TypeError, ne, xn, e)
     438  
     439  if __name__ == "__main__":
     440      unittest.main()