re-use pytest assertion logic for complex objects - python

How can I reuse the python "smart" reporting for objects?
E.g.:
class Foo(object):
def __init__(self, text, sequence):
self.text = text
self.sequence = sequence
def __eq__(self, other):
return type(self) == type(other) and self.text == other.text and self.sequence == other.sequence
def __ne__(self, other):
return not self.__eq__(other)
What I want is to get the assertion report, just as I would assert the primitive fields, e.g. (of course makes sense to not print attributes that satisfy the comparison operator - and concatenate together the failing attribute reports). Something like:
def test_magic():
> assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E assert Foo(text='a', sequence=[1]) == Foo(text='a', sequence=[1, 2])
E attribute `sequence`:
E [1] == [1, 2]
E Right contains more items, first extra item: 2
E Use -v to get the full diff
Can I do this by writing some plugin, etc and calling in that the internal comparator? Or what is the recommended solution?

Related

Python equality of class instances [duplicate]

When writing custom classes it is often important to allow equivalence by means of the == and != operators. In Python, this is made possible by implementing the __eq__ and __ne__ special methods, respectively. The easiest way I've found to do this is the following method:
class Foo:
def __init__(self, item):
self.item = item
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
Do you know of more elegant means of doing this? Do you know of any particular disadvantages to using the above method of comparing __dict__s?
Note: A bit of clarification--when __eq__ and __ne__ are undefined, you'll find this behavior:
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False
That is, a == b evaluates to False because it really runs a is b, a test of identity (i.e., "Is a the same object as b?").
When __eq__ and __ne__ are defined, you'll find this behavior (which is the one we're after):
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True
Consider this simple problem:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
So, Python by default uses the object identifiers for comparison operations:
id(n1) # 140400634555856
id(n2) # 140400634555920
Overriding the __eq__ function seems to solve the problem:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False
n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3
In Python 2, always remember to override the __ne__ function as well, as the documentation states:
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.
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
In Python 3, this is no longer necessary, as the documentation states:
By default, __ne__() delegates to __eq__() and inverts the result
unless it is NotImplemented. There are no other implied
relationships among the comparison operators, for example, the truth
of (x<y or x==y) does not imply x<=y.
But that does not solve all our problems. Let’s add a subclass:
class SubNumber(Number):
pass
n3 = SubNumber(1)
n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False
Note: Python 2 has two kinds of classes:
classic-style (or old-style) classes, that do not inherit from object and that are declared as class A:, class A(): or class A(B): where B is a classic-style class;
new-style classes, that do inherit from object and that are declared as class A(object) or class A(B): where B is a new-style class. Python 3 has only new-style classes that are declared as class A:, class A(object): or class A(B):.
For classic-style classes, a comparison operation always calls the method of the first operand, while for new-style classes, it always calls the method of the subclass operand, regardless of the order of the operands.
So here, if Number is a classic-style class:
n1 == n3 calls n1.__eq__;
n3 == n1 calls n3.__eq__;
n1 != n3 calls n1.__ne__;
n3 != n1 calls n3.__ne__.
And if Number is a new-style class:
both n1 == n3 and n3 == n1 call n3.__eq__;
both n1 != n3 and n3 != n1 call n3.__ne__.
To fix the non-commutativity issue of the == and != operators for Python 2 classic-style classes, the __eq__ and __ne__ methods should return the NotImplemented value when an operand type is not supported. The documentation defines the NotImplemented value as:
Numeric methods and 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.) Its truth value is true.
In this case the operator delegates the comparison operation to the reflected method of the other operand. The documentation defines reflected methods as:
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.
The result looks like this:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is NotImplemented:
return NotImplemented
return not x
Returning the NotImplemented value instead of False is the right thing to do even for new-style classes if commutativity of the == and != operators is desired when the operands are of unrelated types (no inheritance).
Are we there yet? Not quite. How many unique numbers do we have?
len(set([n1, n2, n3])) # 3 -- oops
Sets use the hashes of objects, and by default Python returns the hash of the identifier of the object. Let’s try to override it:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
The end result looks like this (I added some assertions at the end for validation):
class Number:
def __init__(self, number):
self.number = number
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
class SubNumber(Number):
pass
n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)
assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1
assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1
assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1
assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2
You need to be careful with inheritance:
>>> class Foo:
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
>>> class Bar(Foo):pass
>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False
Check types more strictly, like this:
def __eq__(self, other):
if type(other) is type(self):
return self.__dict__ == other.__dict__
return False
Besides that, your approach will work fine, that's what special methods are there for.
The way you describe is the way I've always done it. Since it's totally generic, you can always break that functionality out into a mixin class and inherit it in classes where you want that functionality.
class CommonEqualityMixin(object):
def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
def __ne__(self, other):
return not self.__eq__(other)
class Foo(CommonEqualityMixin):
def __init__(self, item):
self.item = item
Not a direct answer but seemed relevant enough to be tacked on as it saves a bit of verbose tedium on occasion. Cut straight from the docs...
functools.total_ordering(cls)
Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved in specifying all of the possible rich comparison operations:
The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method.
New in version 2.7
#total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
You don't have to override both __eq__ and __ne__ you can override only __cmp__ but this will make an implication on the result of ==, !==, < , > and so on.
is tests for object identity. This means a is b will be True in the case when a and b both hold the reference to the same object. In python you always hold a reference to an object in a variable not the actual object, so essentially for a is b to be true the objects in them should be located in the same memory location. How and most importantly why would you go about overriding this behaviour?
Edit: I didn't know __cmp__ was removed from python 3 so avoid it.
From this answer: https://stackoverflow.com/a/30676267/541136 I have demonstrated that, while it's correct to define __ne__ in terms __eq__ - instead of
def __ne__(self, other):
return not self.__eq__(other)
you should use:
def __ne__(self, other):
return not self == other
I think that the two terms you're looking for are equality (==) and identity (is). For example:
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True <-- a and b have values which are equal
>>> a is b
False <-- a and b are not the same list object
The 'is' test will test for identity using the builtin 'id()' function which essentially returns the memory address of the object and therefore isn't overloadable.
However in the case of testing the equality of a class you probably want to be a little bit more strict about your tests and only compare the data attributes in your class:
import types
class ComparesNicely(object):
def __eq__(self, other):
for key, value in self.__dict__.iteritems():
if (isinstance(value, types.FunctionType) or
key.startswith("__")):
continue
if key not in other.__dict__:
return False
if other.__dict__[key] != value:
return False
return True
This code will only compare non function data members of your class as well as skipping anything private which is generally what you want. In the case of Plain Old Python Objects I have a base class which implements __init__, __str__, __repr__ and __eq__ so my POPO objects don't carry the burden of all that extra (and in most cases identical) logic.
Instead of using subclassing/mixins, I like to use a generic class decorator
def comparable(cls):
""" Class decorator providing generic comparison functionality """
def __eq__(self, other):
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self.__eq__(other)
cls.__eq__ = __eq__
cls.__ne__ = __ne__
return cls
Usage:
#comparable
class Number(object):
def __init__(self, x):
self.x = x
a = Number(1)
b = Number(1)
assert a == b
This incorporates the comments on Algorias' answer, and compares objects by a single attribute because I don't care about the whole dict. hasattr(other, "id") must be true, but I know it is because I set it in the constructor.
def __eq__(self, other):
if other is self:
return True
if type(other) is not type(self):
# delegate to superclass
return NotImplemented
return other.id == self.id
I wrote a custom base with a default implementation of __ne__ that simply negates __eq__:
class HasEq(object):
"""
Mixin that provides a default implementation of ``object.__neq__`` using the subclass's implementation of ``object.__eq__``.
This overcomes Python's deficiency of ``==`` and ``!=`` not being symmetric when overloading comparison operators
(i.e. ``not x == y`` *does not* imply that ``x != y``), so whenever you implement
`object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_, it is expected that you
also implement `object.__ne__ <https://docs.python.org/2/reference/datamodel.html#object.__ne__>`_
NOTE: in Python 3+ this is no longer necessary (see https://docs.python.org/3/reference/datamodel.html#object.__ne__)
"""
def __ne__(self, other):
"""
Default implementation of ``object.__ne__(self, other)``, delegating to ``self.__eq__(self, other)``.
When overriding ``object.__eq__`` in Python, one should also override ``object.__ne__`` to ensure that
``not x == y`` is the same as ``x != y``
(see `object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_ spec)
:return: ``NotImplemented`` if ``self.__eq__(other)`` returns ``NotImplemented``, otherwise ``not self.__eq__(other)``
"""
equal = self.__eq__(other)
# the above result could be either True, False, or NotImplemented
if equal is NotImplemented:
return NotImplemented
return not equal
If you inherit from this base class, you only have to implement __eq__ and the base.
In retrospect, a better approach might have been to implement it as a decorator instead. Something like #functools.total_ordering

How to check if an object exists inside a list of objects?

I am using Python to implement an Earley Parser that has Context Free rules defined as follows:
class Rule:
def __init__(self,string,i,j,dot):
self.i = 0
self.j = 0
self.dot = 0
string = string.split('->')
self.lhs = string[0].strip()
self.rhs1 = string[1].strip()
self.rhs = []
self.rhs1 = self.rhs1.split(' ')
for word in self.rhs1:
if word.strip()!= '':
self.rhs.append(word)
def __eq__(self, other):
if self.i == other.i:
if self.j == other.j:
if self.dot == other.dot:
if self.lhs == other.lhs:
if self.rhs == other.rhs:
return True
return False
To check whether an object of class Rule exists within a chart array or not, I have used the following:
def enqueue(self, entry, state):
if state in self.chart[entry]:
return None
else:
self.chart[entry].append(state)
where chart is an array that is supposed to contain lists of objects of class Rule:
def __init__(self, words):
self.chart = [[] for i in range(len(words))]
Further I check whether a rule exists as that in the chart[entry] as follows (and if it does not exist, then simply append):
def enqueue(self, entry, state):
if state in self.chart[entry]:
return None
else:
self.chart[entry].append(state)
However this gives me an error as
TypeError: 'in <string>' requires string as left operand, not classobj
To circumvent this, I even declared an __eq__ function in the class itself but it doesn't seem to work. Can anyone help me with the same?
Assuming that your object has only a title attribute which is relevant for equality, you have to implement the __eq__ method as follows:
class YourObject:
[...]
def __eq__(self, other):
return self.title == other.title
Of course if you have more attributes that are relevant for equality, you must include those as well. You might also consider implementing __ne__ and __cmp__ for consistent behaviour.

Overloading __eq__ in a class

I'm trying to overload the == operator in a class, and this is the init method:
class Point:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
self._fields = ['a','b','c','d']
And I'm trying to overload the == operator, and here is my code for that:
def __eq__(self,right):
if type(right) == type(self):
for i in self._fields:
print(self._fields.index(i))
else:
return False
return True
For == to be true, all the values in init should be the same. So if I have test=Point(1,2,3), and then I have test2 = Point(1,2,3), then test==test2 should return True. However, I have test=Point(1,2,3), and test2=Point(1,1,3) and this is returning True. Can anybody figure out why this is?
You're testing whether self['a'] == right['a'] when what you want is self.a == right.a. You should use the getattr function to do what you want to do.
Currently, all your code does while iterating over the fields is print out their indexes. It only will ever return False for objects of different types. Instead, you should use getattr to get the actual attribute values that correspond to the names in _fields:
def __eq__(self, other):
return (self._fields == other._fields and
all(getattr(self, attr) == getattr(other, attr) for attr in self._fields)
Note that I've changed the test for the two objects having the same type for one that checks that they have the same field (this is a kind of duck-typing). If you want to stick with a type check, I'd make _fields a class attribute, so you will know that every instance has the same value for it.
Alternately, you could do away with the _fields attribute completely and just hard code the attribute names:
def __eq__(self, other):
return (type(self) == type(other) and
self.a == other.a and self.b == other.b and
self.c == other.c and self.d == other.d)

Elegant ways to support equivalence ("equality") in Python classes

When writing custom classes it is often important to allow equivalence by means of the == and != operators. In Python, this is made possible by implementing the __eq__ and __ne__ special methods, respectively. The easiest way I've found to do this is the following method:
class Foo:
def __init__(self, item):
self.item = item
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
Do you know of more elegant means of doing this? Do you know of any particular disadvantages to using the above method of comparing __dict__s?
Note: A bit of clarification--when __eq__ and __ne__ are undefined, you'll find this behavior:
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False
That is, a == b evaluates to False because it really runs a is b, a test of identity (i.e., "Is a the same object as b?").
When __eq__ and __ne__ are defined, you'll find this behavior (which is the one we're after):
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True
Consider this simple problem:
class Number:
def __init__(self, number):
self.number = number
n1 = Number(1)
n2 = Number(1)
n1 == n2 # False -- oops
So, Python by default uses the object identifiers for comparison operations:
id(n1) # 140400634555856
id(n2) # 140400634555920
Overriding the __eq__ function seems to solve the problem:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return False
n1 == n2 # True
n1 != n2 # True in Python 2 -- oops, False in Python 3
In Python 2, always remember to override the __ne__ function as well, as the documentation states:
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.
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
return not self.__eq__(other)
n1 == n2 # True
n1 != n2 # False
In Python 3, this is no longer necessary, as the documentation states:
By default, __ne__() delegates to __eq__() and inverts the result
unless it is NotImplemented. There are no other implied
relationships among the comparison operators, for example, the truth
of (x<y or x==y) does not imply x<=y.
But that does not solve all our problems. Let’s add a subclass:
class SubNumber(Number):
pass
n3 = SubNumber(1)
n1 == n3 # False for classic-style classes -- oops, True for new-style classes
n3 == n1 # True
n1 != n3 # True for classic-style classes -- oops, False for new-style classes
n3 != n1 # False
Note: Python 2 has two kinds of classes:
classic-style (or old-style) classes, that do not inherit from object and that are declared as class A:, class A(): or class A(B): where B is a classic-style class;
new-style classes, that do inherit from object and that are declared as class A(object) or class A(B): where B is a new-style class. Python 3 has only new-style classes that are declared as class A:, class A(object): or class A(B):.
For classic-style classes, a comparison operation always calls the method of the first operand, while for new-style classes, it always calls the method of the subclass operand, regardless of the order of the operands.
So here, if Number is a classic-style class:
n1 == n3 calls n1.__eq__;
n3 == n1 calls n3.__eq__;
n1 != n3 calls n1.__ne__;
n3 != n1 calls n3.__ne__.
And if Number is a new-style class:
both n1 == n3 and n3 == n1 call n3.__eq__;
both n1 != n3 and n3 != n1 call n3.__ne__.
To fix the non-commutativity issue of the == and != operators for Python 2 classic-style classes, the __eq__ and __ne__ methods should return the NotImplemented value when an operand type is not supported. The documentation defines the NotImplemented value as:
Numeric methods and 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.) Its truth value is true.
In this case the operator delegates the comparison operation to the reflected method of the other operand. The documentation defines reflected methods as:
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.
The result looks like this:
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is NotImplemented:
return NotImplemented
return not x
Returning the NotImplemented value instead of False is the right thing to do even for new-style classes if commutativity of the == and != operators is desired when the operands are of unrelated types (no inheritance).
Are we there yet? Not quite. How many unique numbers do we have?
len(set([n1, n2, n3])) # 3 -- oops
Sets use the hashes of objects, and by default Python returns the hash of the identifier of the object. Let’s try to override it:
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
len(set([n1, n2, n3])) # 1
The end result looks like this (I added some assertions at the end for validation):
class Number:
def __init__(self, number):
self.number = number
def __eq__(self, other):
"""Overrides the default implementation"""
if isinstance(other, Number):
return self.number == other.number
return NotImplemented
def __ne__(self, other):
"""Overrides the default implementation (unnecessary in Python 3)"""
x = self.__eq__(other)
if x is not NotImplemented:
return not x
return NotImplemented
def __hash__(self):
"""Overrides the default implementation"""
return hash(tuple(sorted(self.__dict__.items())))
class SubNumber(Number):
pass
n1 = Number(1)
n2 = Number(1)
n3 = SubNumber(1)
n4 = SubNumber(4)
assert n1 == n2
assert n2 == n1
assert not n1 != n2
assert not n2 != n1
assert n1 == n3
assert n3 == n1
assert not n1 != n3
assert not n3 != n1
assert not n1 == n4
assert not n4 == n1
assert n1 != n4
assert n4 != n1
assert len(set([n1, n2, n3, ])) == 1
assert len(set([n1, n2, n3, n4])) == 2
You need to be careful with inheritance:
>>> class Foo:
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
>>> class Bar(Foo):pass
>>> b = Bar()
>>> f = Foo()
>>> f == b
True
>>> b == f
False
Check types more strictly, like this:
def __eq__(self, other):
if type(other) is type(self):
return self.__dict__ == other.__dict__
return False
Besides that, your approach will work fine, that's what special methods are there for.
The way you describe is the way I've always done it. Since it's totally generic, you can always break that functionality out into a mixin class and inherit it in classes where you want that functionality.
class CommonEqualityMixin(object):
def __eq__(self, other):
return (isinstance(other, self.__class__)
and self.__dict__ == other.__dict__)
def __ne__(self, other):
return not self.__eq__(other)
class Foo(CommonEqualityMixin):
def __init__(self, item):
self.item = item
Not a direct answer but seemed relevant enough to be tacked on as it saves a bit of verbose tedium on occasion. Cut straight from the docs...
functools.total_ordering(cls)
Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved in specifying all of the possible rich comparison operations:
The class must define one of __lt__(), __le__(), __gt__(), or __ge__(). In addition, the class should supply an __eq__() method.
New in version 2.7
#total_ordering
class Student:
def __eq__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) ==
(other.lastname.lower(), other.firstname.lower()))
def __lt__(self, other):
return ((self.lastname.lower(), self.firstname.lower()) <
(other.lastname.lower(), other.firstname.lower()))
You don't have to override both __eq__ and __ne__ you can override only __cmp__ but this will make an implication on the result of ==, !==, < , > and so on.
is tests for object identity. This means a is b will be True in the case when a and b both hold the reference to the same object. In python you always hold a reference to an object in a variable not the actual object, so essentially for a is b to be true the objects in them should be located in the same memory location. How and most importantly why would you go about overriding this behaviour?
Edit: I didn't know __cmp__ was removed from python 3 so avoid it.
From this answer: https://stackoverflow.com/a/30676267/541136 I have demonstrated that, while it's correct to define __ne__ in terms __eq__ - instead of
def __ne__(self, other):
return not self.__eq__(other)
you should use:
def __ne__(self, other):
return not self == other
I think that the two terms you're looking for are equality (==) and identity (is). For example:
>>> a = [1,2,3]
>>> b = [1,2,3]
>>> a == b
True <-- a and b have values which are equal
>>> a is b
False <-- a and b are not the same list object
The 'is' test will test for identity using the builtin 'id()' function which essentially returns the memory address of the object and therefore isn't overloadable.
However in the case of testing the equality of a class you probably want to be a little bit more strict about your tests and only compare the data attributes in your class:
import types
class ComparesNicely(object):
def __eq__(self, other):
for key, value in self.__dict__.iteritems():
if (isinstance(value, types.FunctionType) or
key.startswith("__")):
continue
if key not in other.__dict__:
return False
if other.__dict__[key] != value:
return False
return True
This code will only compare non function data members of your class as well as skipping anything private which is generally what you want. In the case of Plain Old Python Objects I have a base class which implements __init__, __str__, __repr__ and __eq__ so my POPO objects don't carry the burden of all that extra (and in most cases identical) logic.
Instead of using subclassing/mixins, I like to use a generic class decorator
def comparable(cls):
""" Class decorator providing generic comparison functionality """
def __eq__(self, other):
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self.__eq__(other)
cls.__eq__ = __eq__
cls.__ne__ = __ne__
return cls
Usage:
#comparable
class Number(object):
def __init__(self, x):
self.x = x
a = Number(1)
b = Number(1)
assert a == b
This incorporates the comments on Algorias' answer, and compares objects by a single attribute because I don't care about the whole dict. hasattr(other, "id") must be true, but I know it is because I set it in the constructor.
def __eq__(self, other):
if other is self:
return True
if type(other) is not type(self):
# delegate to superclass
return NotImplemented
return other.id == self.id
I wrote a custom base with a default implementation of __ne__ that simply negates __eq__:
class HasEq(object):
"""
Mixin that provides a default implementation of ``object.__neq__`` using the subclass's implementation of ``object.__eq__``.
This overcomes Python's deficiency of ``==`` and ``!=`` not being symmetric when overloading comparison operators
(i.e. ``not x == y`` *does not* imply that ``x != y``), so whenever you implement
`object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_, it is expected that you
also implement `object.__ne__ <https://docs.python.org/2/reference/datamodel.html#object.__ne__>`_
NOTE: in Python 3+ this is no longer necessary (see https://docs.python.org/3/reference/datamodel.html#object.__ne__)
"""
def __ne__(self, other):
"""
Default implementation of ``object.__ne__(self, other)``, delegating to ``self.__eq__(self, other)``.
When overriding ``object.__eq__`` in Python, one should also override ``object.__ne__`` to ensure that
``not x == y`` is the same as ``x != y``
(see `object.__eq__ <https://docs.python.org/2/reference/datamodel.html#object.__eq__>`_ spec)
:return: ``NotImplemented`` if ``self.__eq__(other)`` returns ``NotImplemented``, otherwise ``not self.__eq__(other)``
"""
equal = self.__eq__(other)
# the above result could be either True, False, or NotImplemented
if equal is NotImplemented:
return NotImplemented
return not equal
If you inherit from this base class, you only have to implement __eq__ and the base.
In retrospect, a better approach might have been to implement it as a decorator instead. Something like #functools.total_ordering

Extending base classes in Python

I'm trying to extend some "base" classes in Python:
class xlist (list):
def len(self):
return len(self)
def add(self, *args):
self.extend(args)
return None
class xint (int):
def add(self, value):
self += value
return self
x = xlist([1,2,3])
print x.len() ## >>> 3 ok
print x ## >>> [1,2,3] ok
x.add (4, 5, 6)
print x ## >>> [1,2,3,4,5,6] ok
x = xint(10)
print x ## >>> 10 ok
x.add (2)
print x ## >>> 10 # Not ok (#1)
print type(x) ## >>> <class '__main__.xint'> ok
x += 5
print type(x) ## >>> <type 'int'> # Not ok (#2)
It works fine in the list case because the append method modifies the object "in place", without returning it. But in the int case, the add method doesn't modify the value of the external x variable. I suppose that's fine in the sense that self is a local variable in the add method of the class, but this is preventing me from modifying the initial value assigned to the instance of the class.
Is it possible to extend a class this way or should I define a class property with the base type and map all the needed methods to this property?
Your two xint examples don't work for two different reasons.
The first doesn't work because self += value is equivalent to self = self + value which just reassigns the local variable self to a different object (an integer) but doesn't change the original object. You can't really get this
>>> x = xint(10)
>>> x.add(2)
to work with a subclass of int since integers are immutable.
To get the second one to work you can define an __add__ method, like so:
class xint(int):
def __add__(self, value):
return xint(int.__add__(self, value))
>>> x = xint(10)
>>> type(x)
<class '__main__.xint'>
>>> x += 3
>>> x
13
>>> type(x)
<class '__main__.xint'>
int is a value type, so each time you do an assignment, (e.g. both instances of += above), it doesn't modify the object you have on the heap, but replaces the reference with one of the result of the right hand side of the assignment (i.e. an int)
list isn't a value type, so it isn't bound by the same rules.
this page has more details on the differences: The Python Language Reference - 3. Data model
IMO, yes, you should define a new class that keeps an int as an instance variable
i expanded you xlist class just a bit, made it so you could find all index points of a number making it so you can extend with multiple lists at once making it initialize and making it so you can iterate through it
class xlist:
def __init__(self,alist):
if type(alist)==type(' '):
self.alist = [int(i) for i in alist.split(' ')]
else:
self.alist = alist
def __iter__(self):
i = 0
while i<len(self.alist):
yield self.alist[i]
i+=1
def len(self):
return len(self.alist)
def add(self, *args):
if type(args[0])==type([1]):
if len(args)>1:
tmp = []
[tmp.extend(i) for i in args]
args = tmp
else:args = args[0]
if type(args)==type(''):args = [int(i) for i in args.split(' ')]
(self.alist).extend(args)
return None
def index(self,val):
gen = (i for i,x in enumerate(self.alist) if x == val)
return list(gen)
Ints are immutable and you can't modify them in place, so you should go with option #2 (because option #1 is impossible without some trickery).
I wrote an example of a mutable integer class that implements some basic methods from the list of operator methods. It can print properly, add, subtract, multiply, divide, sort, and compare equality.
If you want it to do everything an int can you'll have to implement more methods.
class MutablePartialInt:
def __init__(self, value):
self.value = value
def _do_relational_method(self, other, method_to_run):
func = getattr(self.value, method_to_run)
if type(other) is MutablePartialInt:
return func(other.value)
else:
return func(other)
def __add__(self, other):
return self._do_relational_method(other, "__add__")
def __sub__(self, other):
return self._do_relational_method(other, "__sub__")
def __mul__(self, other):
return self._do_relational_method(other, "__mul__")
def __truediv__(self, other):
return self._do_relational_method(other, "__truediv__")
def __floordiv__(self, other):
return self._do_relational_method(other, "__floordiv__")
def __eq__(self, other):
return self._do_relational_method(other, "__eq__")
def __neq__(self, other):
return self._do_relational_method(other, "__neq__")
def __lt__(self, other):
return self._do_relational_method(other, "__lt__")
def __gt__(self, other):
return self._do_relational_method(other, "__gt__")
def __str__(self):
return str(self.value)
def __repr__(self):
return self.__str__()

Categories

Resources