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

______________________

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix