How can I make my code shorter and keep all functionalities? (Don't repeat yourself)

60 views Asked by At

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 - write a dry code. Did I implement some functionality (for example magic method) twice?

How 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.

import numpy as np
import load_names
import enum
from merge_sort import mergesort




NAMES = load_names.load_names()  # Variables in the global namespace 


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())


#print(create_persons_list())
1

There are 1 answers

2
Abhijeet Kumar On

Yeah, you can make the code more concise.

  1. Instead of a series of 'if-elif' statements in the 'getitem' method, you can use a dictionary to map indices to attributes.

    def __getitem__(self, index):
        attributes = {0: self._name, 1: self._age, 2: self._weight, 3: self._height}
        if index in attributes:
            return attributes[index]
        raise IndexError("Index out of range")
    
  2. Instead of having separate conditions for each sort key, you can use a dictionary to map sort keys to corresponding lambda functions.

    def create_persons_list(n=10, sort_key='height'):
        key_mapping = {'name': 'get_name', 'age': 'get_age', 'height': 'get_height', 
                       'weight': 'get_weight'}
        key_func = lambda x: getattr(x, key_mapping.get(sort_key, 'to_tuple'))()
        return mergesort([Person(np.random.choice(NAMES), np.random.randint(18, 
                         101), np.random.randint(150, 201), np.random.randint(45, 
                         101)) for _ in range(n)], key=key_func)