class Inner():
def __init__(self, x):
self.x = x
def __eq__(self, other):
if isinstance(other, Inner):
return self.x == other.x
else:
raise TypeError("Incorrect type to compare")
class Outer():
def __init__(self, y):
self.y = Inner(y)
def __eq__(self, other):
if isinstance(other, Outer):
return self.y == other.y
elif isinstance(other, Inner):
return self.y == other
else:
raise TypeError("Incorrect type to compare")
if __name__ == "__main__":
a = Outer(1)
b = Inner(1)
print(a == b) # ok no problem
print(b == a) # This will raise a type error
In the example I have inner and outer class. I have no control over what Inner implements just wanted to simulate the situation. I have only control over Outer's behavior. I want Outer instances to be able to compare to Inner instances (not just equality). With the given implementation only the first comparison works because that is calling Outer's __eq__ method allowed to be compared to Outer and Inner instances but the second one is calling Inner's __eq__ which will not allow the comparison to Outer - heck it doesn't know Outer exists why should it bother to implement it.
Is there a way to get the second type of comparison to work, with something similar like the __radd__ and such functions.
I know for instance in C++ you resolve this with inline operator definitions, but we don't have such in Python.
Not to put too fine a point on it: Inner.__eq__ is broken. At the very least, rather than throwing an error it should return NotImplemented, which would allow Python to try the reverse comparison:
When NotImplemented is returned, the interpreter will then try the
reflected operation on the other type, or some other fallback,
depending on the operator. If all attempted operations return
NotImplemented, the interpreter will raise an appropriate exception.
Better yet it would use "duck typing", rather than insisting on a specific class (unless the class, rather than its interface, is an explicitly important part of the comparison):
def __eq__(self, other):
try:
return self.x == other.x
except AttributeError:
return NotImplemented
However, as you say you cannot control this, you will have to manually implement similar functionality, for example:
def compare(a, b):
"""'Safe' comparison between two objects."""
try:
return a == b
except TypeError:
return b == a
as there is no such thing as __req__ in Python's data model.
Related
As far as I understand, in Python, comparing the instances of two different classes for equality, does:
evaluate the __eq__ method of the first instance and return the result
Except if the result is NotImplemented, then evaluate the __eq__ method of the second instance and return the result
Except if the result is NotImplemented, then compare for object identity and return the result (which is False)
I encountered multiple times situations where a raised Exception would have helped me to spot a bug in my code. Martijn Pieters has sketched in this post a way to do that, but also mentioned that it's unpythonic.
Besides being unpythonic, are there actual problems arising from this approach?
Strictly speaking, it is, of course, "unpythonic" because Python encourages duck typing alongside strict subtyping. But I personally prefer strictly typed interfaces, so I throw TypeErrors. Never had any issues, but I can't difinitively say that you will never have any.
Theoretically, I can imagine it being a problem if you find yourself in need to use a mixed-type container like list[Optional[YourType]] and then compare its elements, maybe indirectly, like in constructing set(your_mixed_list).
It would cause problems for dictionaries with mixed keys that have the same hash. Dictionaries allow several keys to have the same hash, and then do an equality check to distinguish keys. See https://stackoverflow.com/a/9022835/1217284
The following script demonstrates this, it fails when the class Misbehaving1 is used:
#!/usr/bin/env python3
class Behaving1:
def __init__(self, value):
self.value = value
def __hash__(self):
return 7
class Behaving2:
def __init__(self, value):
self.value = value
def __hash__(self):
return 7
def __eq__(self, other):
if not (isinstance(other, type(self)) or isinstance(self, type(other))):
return NotImplemented
return self.value == other.value
class MisBehaving1:
def __init__(self, value):
self.value = value
def __hash__(self):
return 7
def __eq__(self, other):
if not (isinstance(other, type(self)) or isinstance(self, type(other))):
raise TypeError(f"Cannot compare {self} of type {type(self)} with {other} of type {type(other)}")
return self.value == other.value
d = {Behaving1(5): 'hello', Behaving2(4): 'world'}
print(d)
dd = {Behaving1(5): 'hello', MisBehaving1(4): 'world'}
print(dd)
Is there ever a reason not to do this to compare two objects:
def __eq__(self, other):
return self.__dict__ == other.__dict__
as opposed to checking each individual attribute:
def __eq__(self, other):
return self.get_a() == other.get_a() and self.get_b() == other.get_b() and ...
Initially I had the latter, but figured the former was the cleaner solution.
You can be explicit and concise:
def __eq__(self, other):
fetcher = operator.attrgetter("a", "b", "c", "d")
try:
return self is other or fetcher(self) == fetcher(other)
except AttributeError:
return False
Just comparing the __dict__ attribute (which might not exist if __slots__ is used) leaves you open to the risk that an unexpected attribute exists on the object:
class A:
def __init__(self, a):
self.a = a
def __eq__(self, other):
return self.__dict__ == other.__dict__
a1 = A(5)
a2 = A(5)
a1.b = 3
assert a1 == a2 # Fails
Some comments:
You should include a self is other check, otherwise, under certain conditions, the same object in memory can compare unequal to itself. Here is a demonstration. The instance-check chrisz mentioned in the comments is a good idea as well.
The dicts of self and other probably contain many more items than the attributes you are manually checking for in the second version. Therefore, the first one will be slower.
(Lastly, but not related to the question, we don't write getters and setters in Python. Access attributes directly with the dot-notation, and if something special needs to happen when getting/setting an attribute, use a property.)
Having some trouble understanding why I'm able to re-define (monkey patch) __eq__ outside of a class, but not change its definition through __init__ or in a method:
class SpecialInteger:
def __init__(self,x):
self.x = x
self.__eq__ = self.equals_normal
def equals_normal(self,other):
return self.x == other.x
def equals_special(self,other):
return self.x != other.x
def switch_to_normal(self):
self.__eq__ = self.equals_normal
def switch_to_special(self):
self.__eq__ = self.equals_special
a = SpecialInteger(3)
b = SpecialInteger(3)
print(a == b) # false
a.switch_to_normal()
print(a == b) # false
SpecialInteger.__eq__ = SpecialInteger.equals_normal
print(a == b) # true
SpecialInteger.__eq__ = SpecialInteger.equals_special
print(a == b) # false
Am I just using self incorrectly or is there some other reason it works like this?
To do it inside the class, you would simply define the __eq__ method inside of your class.
class SpecialInteger:
def __init__(self,x):
self.x = x
def __eq__(self, other):
# do stuff, call whatever other methods you want
EDIT: I see what you are asking, you wish to override the method (which is a "magic" method) at the instance level. I don't believe this is possible in the base construct of the language, per this discussion.
The reason your monkey patch works in that example is because it is being passed on the Class level, as opposed to the instance level, whereas self is referring to the instance.
Just to add on to an excellent existing answer, but this doesn't work because you are modifying the class instance, and not the class.
In order to get the behavior you desire, you can modify the class during __init__, however, this is woefully inadequate (since it modifies the class, and therefore all instances of the class), and you are better off making those changes visible at the class scope.
For example, the following are equivalent:
class SpecialInteger1:
def __init__(self,x):
self.x = x
self.__class__.__eq__ = self.equals_normal
...
class SpecialInteger2:
def __init__(self,x):
self.x = x
def equals_normal(self,other):
return self.x == other.x
def __eq__(self, other):
return self.equals_normal(other)
You should prefer case SpecialInteger2 in all examples, since it is more explicit about what it does.
However, none of this actually solves the issue you are trying to solve: how can I create a specialized equality comparison at the instance level that I can toggle? The answer is through the use of an enum (in Python 3):
from enum import Enum
class Equality(Enum):
NORMAL = 1
SPECIAL = 2
class SpecialInteger:
def __init__(self, x, eq = Equality.NORMAL):
self.x = x
self.eq = eq
def equals_normal(self, other):
return self.x == other.x
def equals_special(self, other):
return self.x != other.x
def __eq__(self, other):
return self.__comp[self.eq](self, other)
# Define a dictionary for O(1) access
# to call the right method.
__comp = {
Equality.NORMAL: equals_normal,
Equality.SPECIAL: equals_special
}
Let's walk through this quickly, since there are 3 parts:
An instance member variable of eq, which can be modified dynamically.
An implementation of __eq__ that selects the correct equality function based on the value of self.eq.
A namespace-mangled dictionary (a class/member variable that starts with __, in this case, self.__comp) that allows efficient lookup of the desired equality method.
The dictionary can easily be done-away with, especially for cases where you only wish to support 1-5 different possible comparisons, and replaced with idiomatic if/then statements, however, if you ever wish to support many more comparison options (say, 300), a dictionary will be much more efficient O(1) than if/then comparisons (linear search, O(n)).
If you wish to do this with setters (like in the original example), and actually hide the member functions from the user, you can also do this by directly storing the function as a variable.
All method definitions are defined at class level (literally the name is a key in a dict belonging to the class). This is also true of anything else you put at class level. Which is why for instance a variable assignment outside a method in a class produces a class variable.
The easiest way to keep the same functionality would be to just refer to some other variable from __eq__. It could be some reference variable, or a saved method.
class SpecialInteger:
def __init__(self,x):
self.x = x
self._equal_method = self.equals_normal
# ...
def switch_to_normal(self):
self._equal_method = self.equals_normal
def switch_to_special(self):
self._equal_method = self.equals_special
def __eq__(self, other):
return self._equal_method(other)
Say I have a Person class, with first, middle and last name attributes. I would like to be able to perform two different types of equality checks on Person objects:
Exactly equal based on string comparison of all attributes
Not inconsistent, i.e. "G. Bluth" == "George Oscar Bluth"
I have been toying with the idea of using __eq__ and __ne__ separately for this:
Person('g', '', 'bluth') == Person('george', 'oscar', 'bluth') # False
Person('g', '', 'bluth') != Person('george', 'oscar', 'bluth') # False
It seems like a neat solution, but having != not always return the opposite of == makes me nervous though. Is it considered bad practice? Should I just steer clear of using the operators and just use a method like consistent(self, other)?
Example implementation:
class Person(object):
def __init__(self, first, middle, last):
self.first = first
self.middle = middle
self.last = last
def __eq__(self, other):
if type(other) is type(self):
return self.__dict__ == other.__dict__
return NotImplemented
def __ne__(self, other):
if type(other) is type(self):
return not (self._compatible(self.first, other.first) and
self._compatible(self.middle, other.middle) and
self._compatible(self.last, other.last))
return NotImplemented
def _compatible(self, s, o):
if s and o:
if s == o or (len(s) == 1 and s == o[0]) or (len(o) == 1 and o == s[0]):
return True
return False
return True
Principle of least astonishment: make the inexact match be a named method, not an overloaded operator. Overloading == for exact match is okay, but overloading operators with semantics other than the obvious is likely to cause confusion. Is it terribly hard to type a few more characters and write Person("G. Bluth").could_be(Person("George Oscar Bluth"))?
I am trying to extend a str and override the magic method __cmp__. The below example shows that the magic method __cmp__ is never called when > is used:
class MyStr(str):
def __cmp__(self, other):
print '(was called)',
return int(self).__cmp__(int(other))
print 'Testing that MyStr(16) > MyStr(7)'
print '---------------------------------'
print 'using _cmp__ :', MyStr(16).__cmp__(MyStr(7))
print 'using > :', MyStr(16) > MyStr(7)
when run results in:
Testing that MyStr(16) > MyStr(7)
---------------------------------
using __cmp__ : (was called) 1
using > : False
Obviously, when using the > the underlying "compare" functionality within the builtin is getting called, which in this case is an alphabetization ordering.
Is there a way to override the __cmp__ builtin with magic methods? And if you cannot directly - what is going on here that is different than non-magic methods where you can?
Comparison operators do not call __cmp__ if the corresponding magic method or its counterpart are defined and do not return NotImplemented:
class MyStr(str):
def __gt__(self, other):
print '(was called)',
return int(self) > int(other)
print MyStr(16) > MyStr(7) # True
P.S.: You probably don't want harmless comparisons to throw exceptions:
class MyStr(str):
def __gt__(self, other):
try:
return int(self) > int(other)
except ValueError:
return False