title: Custom chained comparisons
body: <p>Python allows expressions like <code>x &gt; y &gt; z</code>, which, according to the docs, is equivalent to <code>(x &gt; y) and (y &gt; z)</code> except <code>y</code> is only evaluated once. (<a href="https://docs.python.org/3/reference/expressions.html">https://docs.python.org/3/reference/expressions.html</a>)</p>
  
  <p>However, this seems to break if I customize comparison functions. E.g. suppose I have the following class: (Apologies for the large block, but once you read the <code>__eq__</code> method, the rest is trivial.)</p>
  
  <pre><code>class CompareList(list):
      def __repr__(self):
          return "CompareList([" + ",".join(str(x) for x in self) + "])"
  
      def __eq__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] == other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x == other for x in self)
  
      def __ne__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] != other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x != other for x in self)
  
      def __gt__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] &gt; other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x &gt; other for x in self)
  
      def __ge__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] &gt;= other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x &gt;= other for x in self)
  
      def __lt__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] &lt; other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x &lt; other for x in self)
  
      def __le__(self, other):
          if isinstance(other, list):
              return CompareList(self[idx] &lt;= other[idx] for idx in xrange(len(self)))
          else:
              return CompareList(x &lt;= other for x in self)
  </code></pre>
  
  <p>Now I can do fun stuff like <code>CompareList([10, 5]) &gt; CompareList([5, 10])</code> and it will correctly return <code>CompareList([True,False])</code></p>
  
  <p>However, chaining these operations doesn't work nicely:</p>
  
  <pre><code>low = CompareList([1])
  high = CompareList([2])
  print(low &gt; high &gt; low) # returns CompareList([True])
  </code></pre>
  
  <p>Why not? What happens under the hood here? I know it isn't equivalent to <code>(low &gt; high) &gt; low</code> = <code>(False &gt; low)</code> (because that would return False). It could be <code>low &gt; (high &gt; low)</code> but that wouldn't make sense in terms of operator precedence (normally left-to-right).</p>