Comparable classes in Python 3
To make classes comparable, you only need to implement __lt__
and decorate the class with functools.total_ordering
. You should also provide an __eq__
method if possible. This provides the rest of the comparison operators so you don't have to write any of them yourself.
How do I make my class comparable in Python?
How about the following (Pair sortable by key but you can easily define any other way to sort them):
class Pair:
def __init__(self, key, val):
self.key = key
self.value = val
def __eq__(self, other: "Pair"):
return self.key == other.key
def __lt__(self, other: "Pair"):
return self.key < other.key
def __le__(self, other: "Pair"):
return self.key <= other.key
def __gt__(self, other: "Pair"):
return self.key > other.key
def __ge__(self, other: "Pair"):
return self.key >= other.key
def __str__(self):
return f"{self.key}={self.value}"
def __repr__(self):
return f"{self.key}={self.value} ({id(self)})"
test = [
Pair("a2", "1"), Pair("a1", "2"), Pair("b1", "3"),Pair("br", "4")
]
print(sorted(test))
Output:$ python3 ~/tmp/test.py
[a1=2 (4352627216), a2=1 (4352622288), b1=3 (4352627344), br=4 (4352627408)]
To sort by value and then by key if the values are equal you would dp something like: def __lt__(self, other: "Pair"):
if self.value != other.value:
return self.value < other.value
return self.key < other.key
example input/output with the above lt
:# Input
test = [
Pair("a2", "1"), Pair("a1", "2"), Pair("b1", "1"),Pair("br", "4")
]
# Output
[a2=1 (4466773648), b1=1 (4466778768), a1=2 (4466778640), br=4 (4466778832)]
Additionally, if you plan to use the pairs as dictionary keys or in sets you can implement __hash__
. For more operators see here To make objects of a custom class comparable, is it enough to define just a few of the members in `__eq__` and `__lt__` family?
For two objects a
and b
, __cmp__
requires that one of a < b
, a == b
, and a > b
is true. But that might not be the case: consider sets, where it's very common that none of those are true, e.g. {1, 2, 3}
vs {4, 5, 6}
.
So __lt__
and the likes were introduced. But that left Python with two separate ordering mechanisms, which is kind of ridiculous, so the less flexible one was removed in Python 3.
You don't actually have to implement all six comparison methods. You can use the functools.total_ordering
class decorator to help define the rest of the magic comparison methods:
from functools import total_ordering
@total_ordering
class PQItem:
def __init__(self, priority, item):
self.priority = priority
self.item = item
def __eq__(self, other):
return isinstance(other, PQItem) and self.priority == other.priority
def __lt__(self, other):
return isinstance(other, PQItem) and self.priority < other.priority
How to compare two classes/types in python?
Explanation
This is why your comparison doesn't work as expected
>>> class ClassA(object):
... pass
...
>>> class ClassB(object):
... pass
...
>>> type(ClassB)
<class 'type'>
>>> type(ClassA)
<class 'type'>
>>> type(ClassA) == type(ClassB)
True
But why do ClassA
and ClassB
have the same type type
? Quoting the docs:Example:By default, classes are constructed using type(). The class body is
executed in a new namespace and the class name is bound locally to the
result of type(name, bases, namespace).
>>> ClassB
<class '__main__.ClassB'>
>>> type('ClassB', (), {})
<class '__main__.ClassB'>
>>> type(ClassB)
<class 'type'>
>>> type(type('ClassB', (), {}))
<class 'type'>
Getting the type of ClassB
is exactly the same as getting the type of type('ClassB', (), {})
, which is type
.Solutions
Compare them directly (w/out using the type()
function):
>>> ClassA
<class '__main__.ClassA'>
>>> ClassB
<class '__main__.ClassB'>
>>> ClassA == ClassB
False
or initialize them and compare the types of their objects:>>> a = ClassA()
>>> b = ClassB()
>>> type(a)
<class '__main__.ClassA'>
>>> type(b)
<class '__main__.ClassB'>
>>> type(a) == type(b)
False
FWIW you can also use is
in place of ==
(for classes). Enabling comparison for classes
Define or override the comparison operators for the class. http://docs.python.org/reference/expressions.html#notin
Looks like you are on the right track, except you only need to pass the second circle object to your comparison. self refers to the first circle object. So self.r would give you the r of the first circle. Also you need to return True or False from the method.
def __gt__(self, circle2):
return self.r > circle2.r
Note that this is just comparing the r's of the circles. How to use a method in a class to compare two objects of the class
You mix class attributes and instance attributes. Class attributes are shared between instances:
class Banks:
total = 0
def __init__(self,name,money):
self.name=name
self.money=money
Banks.total += money
b1 = Banks("one",100)
b2 = Banks("two",5000000)
# prints 5000100 - all the money of all banks,
# does not help you comparing avgs ov instances at all
print(b1.total)
Output:5000100
You need seperated averages per instance and a function that compares one instance (self
) agiant another
instance:class School:
def __init__(self):
# self.n =int(input()) # not needed - use len(one of your lists)
list_ages = [float(x) for x in input("Gimme ages, space seperated: ").split()]
list_hight = [float(x) for x in input("Gimme hights, space seperated: ").split()]
list_weight = [float(x) for x in input("Gimme weights, space seperated: ").split()]
# shortest list downsizes all other lists
self.list_ages, self.list_hight, self.list_weight = zip(
*zip( list_ages,list_hight,list_weight ))
def get_av(self):
self.avg_age = sum(self.list_ages) / len(self.list_ages)
print(self.avg_age)
self.avg_height = sum(self.list_hight) / len(self.list_hight)
print(self.avg_height)
self.avg_weight = sum(self.list_weight) / len(self.list_weight)
print(self.avg_weight)
return self.avg_age, self.avg_height, self.avg_weight
def compare(self,other):
self.get_av()
other.get_av()
print("Our pupils are younger: ", self.avg_age < other.avg_age)
print("Our pupils are smaller: ", self.avg_height < other.avg_height)
print("Our pupils are lighter: ", self.avg_weight < other.avg_weight)
c = School() # 4 5 6 22 44 66 88 99 20.2 20.2 20.2 20.2 20.2 20.2
d = School() # 100 100 100
c.compare(d)
Output (formatted with newlines in between):Gimme ages, space seperated: 4 5 6
Gimme hights, space seperated: 22 44 66 88 99
Gimme weights, space seperated: 20.2 20.2 20.2 20.2 20.2 20.2
Gimme ages, space seperated: 100
Gimme hights, space seperated: 100
Gimme weights, space seperated: 100
5.0
44.0
20.2
100.0
100.0
100.0
Our pupils are younger: True
Our pupils are smaller: True
Our pupils are lighter: True
More info:- What is the difference between class and instance attributes?
- python class documentation
In Python, how to know whether objects can be compared?
No, there is no such ABC, because an ABC only dictates what attributes are there. ABCs cannot test for the nature of the implementation (or even if those attributes are actually methods).
The presence of comparison methods (__lt__
, __gt__
, __le__
, __ge__
and __eq__
) does not dictate that the class is going to be comparable with everything else. Usually you can only compare objects of the same type or class of types; numbers with numbers for example.
As such, most types* implement the comparison methods but return the NotImplemented
sentinel object when comparing with other incompatible types. Returning NotImplemented
signals to Python to give the right-hand value a say in the matter too. If a.__lt__(b)
returns NotImplemented
then b.__gt__(a)
is tested too.
The base object
provides default implementations for the methods, returning NotImplemented
:
>>> class Foo:
... pass
...
>>> Foo() < Foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Foo() < Foo()
>>> Foo().__lt__
<method-wrapper '__lt__' of Foo object at 0x10f1cf860>
>>> Foo().__lt__(Foo())
NotImplemented
which is exactly what dict.__lt__
does:>>> {}.__lt__({})
NotImplemented
Numbers, however, only return NotImplemented
when the other type is not comparable:>>> (1).__lt__(2)
True
>>> (1).__lt__('2')
NotImplemented
>>> 1 < 2
True
>>> 1 < '2'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
As such, your best choice is to simply catch the TypeError
thrown when values are not comparable.* I am not aware of any types in the Python 3 standard library that do not implement the comparison methods at this time.
Related Topics
Apt Command Line Interface-Like Yes/No Input
How to Suppress or Capture the Output of Subprocess.Run()
What Is the Purpose of Meshgrid in Python/Numpy
Building a Minimal Plugin Architecture in Python
Best Way to Make Django's Login_Required the Default
Converting Strings to Floats in a Dataframe
Counting Cars Opencv + Python Issue
Pandas Dataframe Column to List
Numpy: Find Index of the Elements Within Range
How to Update SQLalchemy Row Entry
Use Scikit-Learn to Classify into Multiple Categories
"Permission Denied" Trying to Run Python on Windows 10
Numpy 1.21.2 May Not Yet Support Python 3.10
Logisticregression: Unknown Label Type: 'Continuous' Using Sklearn in Python
What's the Difference Between Subprocess Popen and Call (How to Use Them)