I'm coming to Python from Racket. In Racket, I would define a Point structure like this:
(struct Point (x y) #:transparent)
A point is now a structure with two fields named x and y. I can compare two structures for (deep) equality by calling equal?.
What is the equivalent in Python? It looks to me like I have to write twelve lines:
class Point():
def __init__(self,x,y):
self.x = x;
self.y = y;
def __eq__(self, other):
return ((type(other) is Point)
and self.x == other.x
and self.y == other.y)
def __ne__(self, other):
return not(self == other)
... but surely there's an easier way?
Yes, well, if you need an entire class to represent your data type, then you will have to rely on the __eq__ and related dunder methods. However, in this particular case, a Pythonista would use a namedtuple:
from collections import namedtuple
Point = namedtuple('Point', ['x','y'])
Which will inherit all that from tuple.
If you don't need mutability, the simplest way to make basic classes of this sort is collections.namedtuple:
import collections
Point = collections.namedtuple('Point', 'x y')
That's it. You can just make Point objects with pt = Point(1, 2) or the like, and they work like two-tuples, but they also let you access them via named attributes, e.g. pt.x, pt.y.
The equality checking will be a little looser (Point(1, 2) == (1, 2) evaluates to True, because all namedtuples are subclasses of tuple and will compare using tuple rules, and in fact, different subclasses of tuple that don't override the comparison methods will compare equal to each other if they have the same values), but given that tuples are typically used as anonymous lightweight "classes", this is often what you want.
If you need to customize some behavior (adding functionality, or make the type comparisons stricter), you can make a custom class inherit from a namedtuple to get the basic features for free, then customize the bits you care about, e.g., to prevent it testing equal to non-Point types, you can do:
class Point(collections.namedtuple('PointBase', 'x y')):
def __eq__(self, other):
if not isinstance(other, Point):
return False
return super().__eq__(other)
# Sadly, tuple defines __ne__, so you must override it too to behave properly
# You don't need the canonical __ne__ implementation that handles NotImplemented
# though, since you're explicitly unfriendly to non-Point types
def __ne__(self, other): return not (self == other)
Related
I am using a hashable object as a key to a dictionary. The objects are hashable and I can store key-value-pairs in the dict, but when I create a copy of the same object (that gives me the same hash), I get a KeyError.
Here is some small example code:
class Object:
def __init__(self, x): self.x = x
def __hash__(self): return hash(self.x)
o1 = Object(1.)
o2 = Object(1.)
hash(o1) == hash(o2) # This is True
data = {}
data[o1] = 2.
data[o2] # Desired: This should output 2.
In my scenario above, how can I achieve that data[o2] also returns 2.?
You need to implement both __hash__ and __eq__:
class Object:
def __init__(self, x): self.x = x
def __hash__(self): return hash(self.x)
def __eq__(self, other): return self.x == other.x if isinstance(other, self.__class__) else NotImplemented
Per Python documentation:
if a class does not define an __eq__() method it should not define a __hash__() operation either
After finding the hash, Python's dictionary compares the keys using __eq__ and realize they're different, that's why you're not getting the correct output.
You can use the __eq__ magic method to implement a equality check on your object.
def __eq__(self, other):
if (isinstance(other, C)):
return self.x == self.x
You can learn more about magic methods from this link.
So as stated before your object need to implement __ eq__ trait (equality ==), If you want to understand why:
Sometimes hash of different object are the same, this is called collision.
Dictionary manages that by testing if the objects are equals. If they are not dictionary has to manage the collision. How they do that Is implementation details and can vary a lot. A dummy implementation would be list of tuple key value.
Under the hood, a dummy implementation may look like that :
dico[key] = [(object1, value), (object2, value)]
I am in need of a reflected magic method "greater than" and there does not appear to be one. Here is the situation. I have a class which keeps track of units. It is call Property. I have the magic method setup to handle comparisons, but it does not work when I put the Property on the right side. Here is an example:
class Property():
def __init__(self, input, units):
self.value = input
self.units = units
def __gt__(self, other):
if isinstance(other, Property):
return self.value.__gt__(other.value)
else:
return self.value.__gt__(other)
def __float__(self):
return float(self.value)
if __name__=='__main__':
x = Property(1.,'kg')
y = Property(0.,'kg')
print y > x
print float(y) > x
print y > float(x)
So if you run this you will see the output is: False, True, False because the middle example is executing float > Property which uses the built in > not the > I have defined using magic methods. I need a magic method that will be used when the Property is on the right hand side. Is that not a thing? If not, how can I write this so that any combination of values and my own class can be compared. I would like to not have any rules for comparisons. IE, I don't want to just never be able to compare a float to a property.
You can use a functools.total_ordering decorator to create the missing comparison methods for you:
import functools
#functools.total_ordering
class Property():
...
Then you get False, False, False. Do make sure to read its documentation, though.
__lt__ is __gt__'s counterpart; you'll need to implement __lt__. While you're at it, you should probably implement __le__ and __ge__.
Since you've declared the __float__() method, you could always write it as:
print float(y) > float(x)
I am in need of a reflected magic method "greater than" and there does not appear to be one. Here is the situation. I have a class which keeps track of units. It is call Property. I have the magic method setup to handle comparisons, but it does not work when I put the Property on the right side. Here is an example:
class Property():
def __init__(self, input, units):
self.value = input
self.units = units
def __gt__(self, other):
if isinstance(other, Property):
return self.value.__gt__(other.value)
else:
return self.value.__gt__(other)
def __float__(self):
return float(self.value)
if __name__=='__main__':
x = Property(1.,'kg')
y = Property(0.,'kg')
print y > x
print float(y) > x
print y > float(x)
So if you run this you will see the output is: False, True, False because the middle example is executing float > Property which uses the built in > not the > I have defined using magic methods. I need a magic method that will be used when the Property is on the right hand side. Is that not a thing? If not, how can I write this so that any combination of values and my own class can be compared. I would like to not have any rules for comparisons. IE, I don't want to just never be able to compare a float to a property.
You can use a functools.total_ordering decorator to create the missing comparison methods for you:
import functools
#functools.total_ordering
class Property():
...
Then you get False, False, False. Do make sure to read its documentation, though.
__lt__ is __gt__'s counterpart; you'll need to implement __lt__. While you're at it, you should probably implement __le__ and __ge__.
Since you've declared the __float__() method, you could always write it as:
print float(y) > float(x)
I'm looking for the most efficient way of comparing the contents of two class instances. I have a list containing these class instances, and before appending to the list I want to determine if their property values are the same. This may seem trivial to most, but after perusing these forums I wasn't able specific to what I'm trying to do. Also note that I don't have an programming background.
This is what I have so far:
class BaseObject(object):
def __init__(self, name=''):
self._name = name
def __repr__(self):
return '<{0}: \'{1}\'>'.format(self.__class__.__name__, self.name)
def _compare(self, other, *attributes):
count = 0
if isinstance(other, self.__class__):
if len(attributes):
for attrib in attributes:
if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
if self.__dict__[attrib] == other.__dict__[attrib]:
count += 1
return (count == len(attributes))
else:
for attrib in self.__dict__.keys():
if (attrib in self.__dict__.keys()) and (attrib in other.__dict__.keys()):
if self.__dict__[attrib] == other.__dict__[attrib]:
count += 1
return (count == len(self.__dict__.keys()))
def _copy(self):
return (copy.deepcopy(self))
Before adding to my list, I'd do something like:
found = False
for instance in myList:
if instance._compare(newInstance):
found = True
Break
if not found: myList.append(newInstance)
However I'm unclear whether this is the most efficient or python-ic way of comparing the contents of instances of the same class.
Implement a __eq__ special method instead:
def __eq__(self, other, *attributes):
if not isinstance(other, type(self)):
return NotImplemented
if attributes:
d = float('NaN') # default that won't compare equal, even with itself
return all(self.__dict__.get(a, d) == other.__dict__.get(a, d) for a in attributes)
return self.__dict__ == other.__dict__
Now you can just use:
if newInstance in myList:
and Python will automatically use the __eq__ special method to test for equality.
In my version I retained the ability to pass in a limited set of attributes:
instance1.__eq__(instance2, 'attribute1', 'attribute2')
but using all() to make sure we only test as much as is needed.
Note that we return NotImplemented, a special singleton object to signal that the comparison is not supported; Python will ask the other object if it perhaps supports equality testing instead for that case.
You can implement the comparison magic method __eq__(self, other) for your class, then simply do
if instance == newInstance:
As you apparently don't know what attributes your instance will have, you could do:
def __eq__(self, other):
return isinstance(other, type(self)) and self.__dict__ == other.__dict__
Your method has one major flaw: if you have reference cycles with classes that both derive from BaseObject, your comparison will never finish and die with a stack overflow.
In addition, two objects of different classes but with the same attribute values compare as equal. Trivial example: any instance of BaseObject with no attributes will compare as equal to any instance of a BaseObject subclass with no attributes (because if issubclass(C, B) and a is an instance of C, then isinstance(a, B) returns True).
Finally, rather than writing a custom _compare method, just call it __eq__ and reap all the benefits of now being able to use the == operator (including contain testing in lists, container comparisons, etc.).
As a matter of personal preference, though, I'd stay away from that sort-of automatically-generated comparison, and explicitly compare explicit attributes.
I have a class which is a subclass of tuple. I want to use instances of that class as elements of a set, but I get the error that it is an unhashable type. I guess this is because I've overridden the __eq__ and __ne__ methods. What should I do to restore my type's hashability? I'm using Python 3.2.
objects that compare equal should have the same hash value
So it's a good idea to base the hash on the properties you are using to compare equality
Adrien's example would be better like this
class test(tuple):
def __eq__(self,comp):
return self[0] == comp[0]
def __ne__(self,comp):
return self[0] != comp[0]
def __hash__(self):
return hash((self[0],))
Simply leverage the hash of the tuple containing the stuff we care about for equality
you will need to majke your type hashable, which means implementing the __hash__() member function in your class deriving from tuple.
for example:
class test(tuple):
def __eq__(self,comp):
return self[0] == comp[0]
def __ne__(self,comp):
return self[0] != comp[0]
def __hash__(self):
return hash(self[0])
and this is what it looks like now:
>>> set([test([1,]),test([2,]),test([3,])])
{(1,), (2,), (3,)}
>>> hash(test([1,]))
1
note: you should absolutely read the documentation for the __hash__() function, in order to understand the relationship between the comparison operators and the hash computation.