From e087bd49954294b4812db8eb9b00cc18b6748670 Mon Sep 17 00:00:00 2001 From: Adam Parkin Date: Mon, 14 May 2012 15:05:44 -0700 Subject: [PATCH 1/2] Added operator overloading mixin class example --- .../better_ex_using_mixins.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 operator_overloading/better_ex_using_mixins.py diff --git a/operator_overloading/better_ex_using_mixins.py b/operator_overloading/better_ex_using_mixins.py new file mode 100644 index 0000000..ad5b195 --- /dev/null +++ b/operator_overloading/better_ex_using_mixins.py @@ -0,0 +1,61 @@ +''' +Additionally Python supports operator overloading, and (in Python 3) if you +define one of the "magic methods" you get it's converse defined as well (so +for example, if you define __eq__, then you get a sensible __ne__ defined +which uses it). Again, this is Python 3 only. + +Taking all these ideas further, you can create a mixin class where all +comparability is defined in terms of one operator, and then in derived +classes, just inherit from the mixin class and define the one operator, and +you get all operators defined. This file illustrates this. + +Note that this is based upon ideas presented in: + +http://stackoverflow.com/questions/1061283/lt-instead-of-cmp +''' + +class ComparableMixin(object): + ''' + Define sensible defaults for the base "magic methods" in terms of a + less than operator. Any class which inherits from this and defines a + __lt__ operator gets sensible defualts for __gt__, __ge__, __ne__, etc + + Note that this assumes Python 3. If using Python 2.x, then you'll have + to add all the other rich comparison methods (__ge__, __ne__, etc) + ''' + def __eq__(self, other): + return not (self < other or other < self) + + def __le__(self, other): + return not other < self + +class Foo(ComparableMixin): + def __init__(self, val): + self.val = val + super().__init__() + + def __lt__(self, other): + if not isinstance(other, Foo): + raise TypeError('Unorderable types Foo() < {}()'.format(type(other))) + + return self.val < other.val + +if __name__ == "__main__": + f1 = Foo(42) + f2 = Foo(99) + f3 = Foo(42) + + # each spits out True + print(f1 == f1) + print(f1 != f2) + print(f1 == f3) + print(f1 < f2) + print(f2 > f1) + print(f1 <= f3) + print(f1 <= f2) + + try: + f1 <= "This is not a Foo" + except TypeError as e: + print (e) + From e2a37ce2f2a51d5f69df82091d94dd239152ca63 Mon Sep 17 00:00:00 2001 From: Adam Parkin Date: Thu, 7 Jun 2012 14:36:13 -0700 Subject: [PATCH 2/2] Added total_ordering example --- operator_overloading/total_ordering.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 operator_overloading/total_ordering.py diff --git a/operator_overloading/total_ordering.py b/operator_overloading/total_ordering.py new file mode 100644 index 0000000..5891ed5 --- /dev/null +++ b/operator_overloading/total_ordering.py @@ -0,0 +1,40 @@ +''' +Python 3.2 introduced the functools.total_ordering decorator to aid in the +creation of objects which have the rich comparison methods added (lt, eq, +etc). + +If you provide an __eq__ and any one of the other rich comparison methods, +all others are also provided. + +This has also been backported to Python 2.7. + +http://code.activestate.com/recipes/576685-total-ordering-class-decorator/ +''' + +from functools import total_ordering + +@total_ordering +class FooBar(object): + def __init__(self, a, b): + self.a = a + self.b = b + + def __eq__(self, other): + return (self.a, self.b) == (other.a, other.b) + + def __lt__(self, other): + return (self.a, self.b) < (other.a, other.b) + +if __name__ == "__main__": + f1 = FooBar(5, 3) + f2 = FooBar(5, 4) + f3 = FooBar(5, 3) + + assert f1 != f2 + assert f1 == f3 + assert f1 == f1 + assert f1 < f2 + assert f1 <= f2 + assert f1 <= f3 + assert f2 > f1 + assert f2 >= f1