Book Excerpt: The Python Standard Library by Example

3.1.2 Comparison

Under Python 2, classes can define a __cmp__() method that returns -1, 0, or 1 based on whether the object is less than, equal to, or greater than the item being compared. Python 2.1 introduces the rich comparison methods API (__lt__(), __le__(), __eq__(), __ne__(), __gt__(), and __ge__()), which perform a single comparison operation and return a Boolean value. Python 3 deprecated __cmp__() in favor of these new methods, so functools provides tools to make it easier to write Python 2 classes that comply with the new comparison requirements in Python 3.

Rich Comparison

The rich comparison API is designed to allow classes with complex comparisons to implement each test in the most efficient way possible. However, for classes where comparison is relatively simple, there is no point in manually creating each of the rich comparison methods. The total_ordering() class decorator takes a class that provides some of the methods and adds the rest of them.

import functools 
import inspect 
from pprint import pprint 

@functools.total_ordering 
class MyObject(object):
    def __init__(self, val):
        self.val = val 
    def __eq__(self, other): 
        printtesting __eq__(%s, %s)’ % (self.val, other.val) 
        return self.val == other.val 
    def __gt__(self, other):
        print ’ testing __gt__(%s, %s)’ % (self.val, other.val) 
        return self.val > other.val 

print ’Methods:\n’ 
pprint(inspect.getmembers(MyObject, inspect.ismethod)) 

a = MyObject(1) 
b = MyObject(2) 

print \nComparisons:for	expr in [ a < b’, a <= b’, a == b’, a >= b’, a > b’ ]: 
      print \n%-6s:’ % expr 
      result = eval(expr) 
      printresult of %s: <%s’ % (expr, result) 

The class must provide implementation of __eq__() and one other rich comparison method. The decorator adds implementations of the rest of the methods that work by using the comparisons provided.

$	python functools_total_ordering.py 

Methods: 


[(’__eq__’, <unbound method MyObject.__eq__>), 
 (’__ge__’, <unbound method MyObject.__ge__>), 
 (’__gt__’, <unbound method MyObject.__gt__>), 
 (’__init__’, <unbound method MyObject.__init__>), 
 (’__le__’, <unbound method MyObject.__le__>), 
 (’__lt__’, <unbound method MyObject.__lt__>)] 

Comparisons: 

a < b: 
 testing __gt__(2, 1) 
 result of a < b: True 

a <= b:
 testing __gt__(1, 2)
 result of a <= b: True 

a == b: 
 testing __eq__(1, 2)
 result of a == b: False 

a >= b:
 testing __gt__(2, 1)
 result of a >= b: False 

a > b:
 testing __gt__(1, 2)
 result of a > b: False 

Collation Order

Since old-style comparison functions are deprecated in Python 3, the cmp argument to functions like sort() is also no longer supported. Python 2 programs that use comparison functions can use cmp_to_key() to convert them to a function that returns a collation key, which is used to determine the position in the final sequence.

import functools
 
class MyObject(object):
   def __init__(self, val):
       self.val = val 
   def __str__(self): 
      return ’MyObject(%s)’ % self.val 

def compare_obj(a, b): 
    """Old-style comparison function.
    """ 
    print ’comparing %s and %s’ % (a, b) 
    return cmp(a.val, b.val) 

#	Make a key function using cmp_to_key() 
get_key = functools.cmp_to_key(compare_obj) 

def get_key_wrapper(o): 
   """Wrapper function for get_key to allow for print statements.
   """ 
   new_key = get_key(o) 
   print ’key_wrapper(%s) -> %s’ % (o, new_key)
   return new_key 
objs = [ MyObject(x) for x in xrange(5, 0, -1) ] 

for	o in sorted(objs, key=get_key_wrapper):
 print o 

Normally, cmp_to_key() would be used directly, but in this example, an extra wrapper function is introduced to print out more information as the key function is being called.

The output shows that sorted() starts by calling get_key_wrapper() for each item in the sequence to produce a key. The keys returned by cmp_to_key() are instances of a class defined in functools that implements the rich comparison API using the old-style comparison function passed in. After all keys are created, the sequence is sorted by comparing the keys.

$ python functools_cmp_to_key.py 

key_wrapper(MyObject(5)) -> <functools.K object at 0x100da2a50> 
key_wrapper(MyObject(4)) -> <functools.K object at 0x100da2a90> 
key_wrapper(MyObject(3)) -> <functools.K object at 0x100da2ad0> 
key_wrapper(MyObject(2)) -> <functools.K object at 0x100da2b10> 
key_wrapper(MyObject(1)) -> <functools.K object at 0x100da2b50> 
comparing MyObject(4) and MyObject(5) 
comparing MyObject(3) and MyObject(4) 
comparing MyObject(2) and MyObject(3) 
comparing MyObject(1) and MyObject(2) 
MyObject(1) 
MyObject(2) 
MyObject(3) 
MyObject(4) 
MyObject(5) 

See Also:

functools (http://docs.python.org/library/functools.html) The standard library documentation for this module.

Rich comparison methods (http://docs.python.org/reference/datamodel.html# object.__lt__) Description of the rich comparison methods from the Python Reference Guide.

inspect (page 1200) Introspection API for live objects.


© Copyright Pearson Education. All rights reserved.

This excerpt is from the book, ‘The Python Standard Library by Example’ by Doug Hellmann, published by Pearson/Addison-Wesley Professional, June 2011, ISBN 0321767349, Copyright 2011 Pearson Education, Inc. For more info please visit www.informit.com/title/0321767349

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState