Getting Rich With Comparison Methods
-
Upload
matt-story -
Category
Technology
-
view
194 -
download
0
description
Transcript of Getting Rich With Comparison Methods
Getting Rich With Comparison MethodsMatt Story (@mscottstory) // PyGotham – August, 2014
eng.handshake.com@handshakeeng
$2.50
The most certain of all basic principles is that contradictory propositions are not true simultaneously.~ Aristotle -- Metaphisics
>>> from philosopher import Philosopher>>> aristotle = Philosopher.load(“Aristotle”)>>> aristotle == Philosopher.load(“Aristotle”)True
>>> from philosopher import Philosopher>>> aristotle = Philosopher.load(“Aristotle”)>>> aristotle == Philosopher.load(“Aristotle”)True>>> aristotle != Philosopher.load(“Aristotle”)True
How Am I Not Myself?
class Philosopher(JSONStore): '''JSONStore provides `load` and `save`''' store = './lyceum' dump_these = ('name', ‘rep’, 'works’)
def __init__(self, name, rep=5, works=tuple()): self.name = name self.rep = rep self.works = list(works) super(Philosopher, self).__init__(name.lower())
def __eq__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, ‘id’): return self.id == other.id return NotImplemented
class Philosopher(JSONStore): '''JSONStore provides `load` and `save`''' store = './lyceum' dump_these = ('name', ‘rep’, 'works’)
def __init__(self, name, rep=5, works=tuple()): self.name = name self.rep = rep self.works = list(works) super(Philosopher, self).__init__(name.lower())
def __eq__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, ‘id’): return self.id == other.id return NotImplemented
There are no implied relationships among the comparison operators. The truth of x == y does not imply that x != y is false. Accordingly, when defining __eq__(), one should also define __ne__() so that the operators will behave as expected.
5 Minutes Later …
class Philosopher(JSONStore): # ... snip def __eq__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, ‘id’): return self.id == other.id return NotImplemented
def __ne__(self, other): is_eq = self.__eq__(other) if is_eq is NotImplemented: return is_eq return not is_eq
class Philosopher(JSONStore): # ... snip def __eq__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, ‘id’): return self.id == other.id return NotImplemented
def __ne__(self, other): is_eq = self.__eq__(other) if is_eq is NotImplemented: return is_eq return not is_eq
>>> aristotle == Philosopher.load(“Aristotle”)True>>> aristotle != Philosopher.load(“Aristotle”)False
Aristotle
KANT
class Philosopher(JSONStore): # ... Snip def __gt__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, 'rep'): return self.rep > other.rep return NotImplemented
>>> aristotle = Philosopher.load(‘Aristotle’)>>> kant = Philosopher.load(‘Kant’)>>> print aristotle.rep10>>> print kant.rep8>>> aristotle > kantTrue
>>> aristotle = Philosopher.load(‘Aristotle’)>>> kant = Philosopher.load(‘Kant’)>>> print aristotle.rep10>>> print kant.rep8>>> aristotle > kantTrue>>> kant < aristotleTrue
5 Seconds Later …There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection.
class Philosopher(JSONStore): # ... Snip def __gt__(self, other): print ’{}: called __gt__’.format(self.name) if isinstance(self, other.__class__) \ and hasattr(other, 'rep'): return self.rep > other.rep return NotImplemented
>>> aristotle > kantAristotle: called __gt__True>>> kant < aristotleAristotle: called __gt__True
NotImplementedThis type has a single value. There is a single object with this value … Rich comparison methods may return this value if they do not implement the operation for the operands provided. (The interpreter will then try the reflected operation, or some other fallback, depending on the operator.)
>>> "abc".__lt__(0)NotImplemented
Right-Side Reflection Returns
NotImplemented
Left-Side Method Is Defined
Yes
No
Left-Side Method Returns
NotImplemented
Yes
No
Return
Right-Side Reflection Is
Defined
Return
Yes
No
Return Default (is)
No
Aristotle
Foucault
class PostModernist(Philosopher): def __lt__(self, other): if isinstance(self, other.__class__): # with panopticon authority return False return NotImplemented
>>> from philosopher import PostModernist>>> foucault = PostModernist.load('Foucault')>>> foucault.rep6>>> aristotle.rep10>>> aristotle > foucault
>>> from philosopher import PostModernist>>> foucault = PostModernist.load('Foucault')>>> foucault.rep6>>> aristotle.rep10>>> aristotle > foucaultFalse
class PostModernist(Philosopher): def __lt__(self, other): print ’{}: __lt__ called'.format(self.name) if isinstance(self, other.__class__): return False return NotImplemented
>>> aristotle > foucaultFoucault: called __lt__False
import operator
assert operator.lt(kant, aristotle)assert operator.gt(aristotle, kant)assert operator.eq(kant, kant)assert operator.eq(aristotle, kant)
__eq____ne____lt____le____gt____ge__Lots of methods, means lots of redundant code
@functools.total_orderingclass Philosopher(JSONStore): # ... snip def __eq__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, ‘id’): return self.id == other.id return NotImplemented
def __gt__(self, other): if isinstance(self, other.__class__) \ and hasattr(other, 'rep'): return self.rep > other.rep return NotImplemented def __ne__(self, other): # snip ...
class X(object): def __eq__(self, other): return lambda x: x == other
def __ne__(self, other): return lambda x: x != other
def __lt__(self, other): return lambda x: x < other
def __le__(self, other): return lambda x: x <= other
def __gt__(self, other): return lambda x: x > other
def __ge__(self, other): return lambda x: x >= other
x = X()
>>> filter(x == 2, range(5))[2]>>> filter(x > 1, range(5))[2, 3, 4]
Thanks@mscottstory