Skip to content

Commit ea48fcc

Browse files
Update doc for equalsNotEquals
1 parent 58f503d commit ea48fcc

File tree

5 files changed

+66
-48
lines changed

5 files changed

+66
-48
lines changed

python/ql/src/Classes/Comparisons/EqualsOrNotEquals.qhelp

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,47 @@
44
<qhelp>
55

66
<overview>
7-
<p>In order to conform to the object model, classes should define either no equality methods, or both
8-
an equality and an inequality method. If only one of <code>__eq__</code> or <code>__ne__</code> is
9-
defined then the method from the super class is used. This is unlikely to result in the expected
10-
behavior.</p>
7+
<p>In order to ensure the <code>==</code> and <code>!=</code> operators behave consistently as expected (i.e. they should be negations of each other), care should be taken when implementing the
8+
<code>__eq__</code> and <code>__ne__</code> special methods.</p>
9+
10+
<p>In Python 3, if the <code>__eq__</code> method is defined in a class while the <code>__ne__</code> is not,
11+
then the <code>!=</code> operator will automatically delegate to the <code>__eq__</code> method in the expected way.
12+
</p>
13+
14+
<p>However, if the <code>__ne__</code> method is defined without a corresponding <code>__eq__</code> method,
15+
the <code>==</code> operator will still default to object identity (equivalent to the <code>is</code> operator), while the <code>!=</code>
16+
operator will use the <code>__ne__</code> method, which may be inconsistent.
17+
18+
<p>Additionally, if the <code>__ne__</code> method is defined on a superclass, and the subclass defines its own <cde>__eq__</code> method without overriding
19+
the superclass <code>__ne__</code> method, the <code>!=</code> operator will use this superclass <code>__ne__</code> method, rather than automatically delegating
20+
to <code>__eq__</code>, which may be incorrect.
1121

1222
</overview>
1323
<recommendation>
1424

15-
<p>When you define an equality or an inequality method for a class, remember to implement both an
16-
<code>__eq__</code> method and an <code>__ne__</code> method.</p>
25+
<p>Ensure that when an <code>__ne__</code> method is defined, the <code>__eq__</code> method is also defined, and their results are consistent.
26+
In most cases, the <code>__ne__</code> method does not need to be defined at all, as the default behavior is to delegate to <code>__eq__</code> and negate the result. </p>
1727

1828
</recommendation>
1929
<example>
20-
<p>In the following example the <code>PointOriginal</code> class defines an equality method but
21-
no inequality method. If this class is tested for inequality then a type error will be raised. The
22-
<code>PointUpdated</code> class is better as it defines both an equality and an inequality method. To
23-
comply fully with the object model this class should also define a hash method (identified by
24-
a separate rule).</p>
30+
<p>In the following example, <code>A</code> defines a <code>__ne__</code> method, but not an <code>__eq__</code> method.
31+
This leads to inconsistent results between equality and inequality operators.
32+
</p>
33+
34+
<sample src="examples/EqualsOrNotEquals1.py" />
35+
36+
<p>In the following example, <code>C</code> defines an <code>__eq__</code> method, but its <code>__ne__</code> implementation is inherited from <code>B</code>,
37+
which is not consistent with the equality operation.
38+
</p>
2539

26-
<sample src="EqualsOrNotEquals.py" />
40+
<sample src="examples/EqualsOrNotEquals2.py" />
2741

2842
</example>
2943
<references>
3044

3145

32-
<li>Python Language Reference: <a href="http://docs.python.org/2/reference/datamodel.html#object.__ne__">object.__ne__</a>,
33-
<a href="http://docs.python.org/2/reference/expressions.html#comparisons">Comparisons</a>.</li>
46+
<li>Python Language Reference: <a href="http://docs.python.org/3/reference/datamodel.html#object.__ne__">object.__ne__</a>,
47+
<a href="http://docs.python.org/3/reference/expressions.html#comparisons">Comparisons</a>.</li>
3448

3549

3650
</references>

python/ql/src/Classes/Comparisons/IncompleteOrdering.qhelp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ If the ordering is not consistent with default equality, then <code>__eq__</code
1717
</p>
1818

1919
<p>
20-
The <code>functools.total_ordering</code> class decorator can be used to automatically implement all four comparison methods from a single one,
20+
The <code>functools.total_ordering</code> class decorator can be used to automatically implement all four comparison methods from a
21+
single one,
2122
which is typically the cleanest way to ensure all necessary comparison methods are implemented consistently.</p>
2223

2324
</recommendation>

python/ql/src/Classes/Comparisons/examples/EqualsOrNotEquals.py

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class A:
2+
def __init__(self, a):
3+
self.a = a
4+
5+
# BAD: ne is defined, but not eq.
6+
def __ne__(self, other):
7+
if not isinstance(other, A):
8+
return NotImplemented
9+
return self.a != other.a
10+
11+
x = A(1)
12+
y = A(1)
13+
14+
print(x == y) # Prints False (potentially unexpected - object identity is used)
15+
print(x != y) # Prints False
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class B:
2+
def __init__(self, b):
3+
self.b = b
4+
5+
def __eq__(self, other):
6+
return self.b == other.b
7+
8+
def __ne__(self, other):
9+
return self.b != other.b
10+
11+
class C(B):
12+
def __init__(self, b, c):
13+
super().init(b)
14+
self.c = c
15+
16+
# BAD: eq is defined, but != will use superclass ne method, which is not consistent
17+
def __eq__(self, other):
18+
return self.b == other.b and self.c == other.c
19+
20+
print(C(1,2) == C(1,3)) # Prints False
21+
print(C(1,2) != C(1,3)) # Prints False (potentially unexpected)

0 commit comments

Comments
 (0)