I am working on a program that sorts data using mergesort. I implemented functions to 'Person' class to make it comparable, sortable, subscriptable, iterable, and castable (to float, int, and string). I want to define everything once that no code is repeated - i.e. have D.R.Y. code. Did I implement some functionality (for example magic method) twice?

Are there any specific locations that can be shorter? and keep all functionalities at the same time? If so, suggest a change in a specific line.


 class Person():
 
 def __init__(self, name, age, height, weight):
 self._name = name
 self._age = age
 self._height = height
 self._weight = weight
 
 
 #def __len__(self):
 #return len(self.data)
 
 
 
 def __repr__(self):
 return f"{self._name}, {self._age} years, {self._height} cm, {self._weight} kg\n"
 
 def __eq__(self, other):
 return (self._age, self._height, self._weight) == (other._age, other._height, other._weight)
 
 def __lt__(self, other):
 return (self._age, self._height, self._weight) < (other._age, other._height, other._weight)
 
 def __le__(self, other):
 return (self._age, self._height, self._weight) <= (other._age, other._height, other._weight)
 
 def __gt__(self, other):
 return (self._age, self._height, self._weight) > (other._age, other._height, other._weight)
 
 def __ge__(self, other):
 return (self._age, self._height, self._weight) >= (other._age, other._height, other._weight)
 
 
 def __str__(self):
 return f"{self._name}, {self._age} years, {self._height} cm, {self._weight} kg"
 
 def __getitem__(self, index):
 if index == 0:
 return self._name
 elif index == 1:
 return self._age
 elif index == 2:
 return self._weight
 elif index == 3:
 return self._height
 else:
 raise IndexError("Index out of range")
 
 def __int__(self):
 return int(self._age)
 
 def __float__(self):
 return float(self._age)
 
 def to_tuple(self):
 return (self._name, self._age, self._weight, self._height)
 
 def to_list(self):
 return [self._name, self._age, self._weight, self._height]
 def get_name(self):
 return self._name
 def get_age(self):
 return self._age
 def get_height(self):
 return self._height
 def get_weight(self):
 return self._weight
 
 
 
 
 def create_persons_list(n=10, sort_key='height'):
 
 person_objects = [(Person(np.random.choice(NAMES), np.random.randint(18, 101), np.random.randint(150, 201), np.random.randint(45, 101))) for _ in range(n)]
 #print("po: ",person_objects[0])
 x=Person()
 if sort_key == 'name':
 return mergesort(person_objects, key=lambda x: x.get_name())
 elif sort_key == 'age':
 return mergesort(person_objects, key=lambda x: x.get_age())
 elif sort_key == 'height':
 return mergesort(person_objects, key=lambda x: x.get_height())
 elif sort_key == 'weight':
 return mergesort(person_objects, key=lambda x: x.get_weight())
 else:
 return mergesort(person_objects)
 
 # Example usage:
 sorted_persons_by_name = create_persons_list(sort_key='name')
 sorted_persons_by_age = create_persons_list(sort_key='age')
 sorted_persons_by_height = create_persons_list(sort_key='height')
 sorted_persons_by_weight = create_persons_list(sort_key='weight')
 
 
 print("Sorted by height (default): \n")
 print(sorted_persons_by_height)
 print("Sorted by name: \n")
 print(sorted_persons_by_name)
 print("Sorted by age: \n")
 print(sorted_persons_by_age)
 print("Sorted by weight: \n")
 print(sorted_persons_by_weight)
 print("No key arg: ", create_persons_list())