Inconsistent equality and inequality¶
ID: py/inconsistent-equalityKind: problemSecurity severity: Severity: warningPrecision: very-highTags: - quality - reliability - correctnessQuery suites: - python-security-and-quality.qls
Click to see the query in the CodeQL repository
In order to ensure the== and!= operators behave consistently as expected (i.e. they should be negations of each other), care should be taken when implementing the__eq__ and__ne__ special methods.
In Python 3, if the__eq__ method is defined in a class while the__ne__ is not, then the!= operator will automatically delegate to the__eq__ method in the expected way.
However, if the__ne__ method is defined without a corresponding__eq__ method, the== operator will still default to object identity (equivalent to theis operator), while the!= operator will use the__ne__ method, which may be inconsistent.
Additionally, if the__ne__ method is defined on a superclass, and the subclass defines its own__eq__ method without overriding the superclass__ne__ method, the!= operator will use this superclass__ne__ method, rather than automatically delegating to__eq__, which may be incorrect.
Recommendation¶
Ensure that when an__ne__ method is defined, the__eq__ method is also defined, and their results are consistent. In most cases, the__ne__ method does not need to be defined at all, as the default behavior is to delegate to__eq__ and negate the result.
Example¶
In the following example,A defines a__ne__ method, but not an__eq__ method. This leads to inconsistent results between equality and inequality operators.
classA:def__init__(self,a):self.a=a# BAD: ne is defined, but not eq.def__ne__(self,other):ifnotisinstance(other,A):returnNotImplementedreturnself.a!=other.ax=A(1)y=A(1)print(x==y)# Prints False (potentially unexpected - object identity is used)print(x!=y)# Prints False
In the following example,C defines an__eq__ method, but its__ne__ implementation is inherited fromB, which is not consistent with the equality operation.
classB:def__init__(self,b):self.b=bdef__eq__(self,other):returnself.b==other.bdef__ne__(self,other):returnself.b!=other.bclassC(B):def__init__(self,b,c):super().__init__(b)self.c=c# BAD: eq is defined, but != will use superclass ne method, which is not consistentdef__eq__(self,other):returnself.b==other.bandself.c==other.cprint(C(1,2)==C(1,3))# Prints Falseprint(C(1,2)!=C(1,3))# Prints False (potentially unexpected)
References¶
Python Language Reference:object.ne,Comparisons.