Class with many attributes, what would be the 'right' way to do this?

What is the ‘correct’ or ‘right’ way to handle classes with many attributes? I have a class with 37 attributes and it seems quite excessive and not practical.

This class is going to be used to handle inserting this data to a database, retrieve that information from a database and its also going to be used to run some mathematic models.

I thought about putting all that information to a dictionary but accessing these attributes might be easier and clearer if they are attributes rather than if they would be in a dictionary.

What are your thoughts on this? Down below is the class and its init method.

class Statistics:

    def __init__(self, match_id,
                home_ball_possession = None,      away_ball_possession = None,
                home_goal_attempts = None,        away_goal_attempts = None,
                home_shots_on_goal = None,         away_shots_on_goal = None,
                home_shots_off_goal = None,        away_shots_off_goal = None,
                home_blocked_shots = None,        away_blocked_shots = None,
                home_free_kicks = None,           away_free_kicks = None,
                home_corner_kicks = None,         away_corner_kicks = None,
                home_offsides = None,            away_offsides = None,
                home_throw_ins = None,            away_throw_ins = None,
                home_goalkeeper_saves = None,     away_goalkeeper_saves = None,
                home_fouls = None,               away_fouls = None,
                home_red_cards = None,            away_red_cards = None,
                home_yellow_cards = None,         away_yellow_cards = None,
                home_total_passes = None,         away_total_passes = None,
                home_completed_passes = None,     away_completed_passes = None,
                home_tackles = None,             away_tackles = None,
                home_attacks = None,             away_attacks = None,
                home_dangerous_attacks = None,    away_dangerous_attacks = None
             ) -> None:
    
    self.match_id = match_id

    self.home_ball_possession = home_ball_possession
    self.away_ball_possession = away_ball_possession
    
    self.home_goal_attempts = home_goal_attempts
    self.away_goal_attempts = away_goal_attempts

    self.home_shots_on_goal = home_shots_on_goal        
    self.away_shots_on_goal = away_shots_on_goal

    self.home_shots_off_goal = home_shots_off_goal       
    self.away_shots_off_goal = away_shots_off_goal

    self.home_blocked_shots = home_blocked_shots       
    self.away_blocked_shots = away_blocked_shots

    self.home_free_kicks = home_free_kicks          
    self.away_free_kicks = away_free_kicks

    self.home_corner_kicks = home_corner_kicks        
    self.away_corner_kicks = away_corner_kicks

    self.home_offsides = home_offsides           
    self.away_offsides = away_offsides

    self.home_throw_ins = home_throw_ins           
    self.away_throw_ins = away_throw_ins

    self.home_goalkeeper_saves = home_goalkeeper_saves    
    self.away_goalkeeper_saves = away_goalkeeper_saves

    self.home_fouls = home_fouls              
    self.away_fouls = away_fouls

    self.home_red_cards = home_red_cards           
    self.away_red_cards = away_red_cards

    self.home_yellow_cards = home_yellow_cards        
    self.away_yellow_cards = away_yellow_cards

    self.home_total_passes = home_total_passes        
    self.away_total_passes = away_total_passes

    self.home_completed_passes = home_completed_passes    
    self.away_completed_passes = away_completed_passes

    self.home_tackles = home_tackles            
    self.away_tackles = away_tackles

    self.home_attacks = home_attacks         
    self.away_attacks = away_attacks

    self.home_dangerous_attacks = home_dangerous_attacks 
    self.away_dangerous_attacks = away_dangerous_attacks

Edit: changed attribute names from camelCase to lowercase, with words separated by underscores

>Solution :

Use dataclasses, along with typing.

import dataclasses

@dataclasses.dataclass
class Statistics:
    matchId: X
    homeBallPossession: X
    awayBallPossession: X
    ...

Replace all X‘s with type names, e.g. int, str, typing.Optional[int], and so on.

This way, you don’t have to write out a constructor at all, so you don’t have to repeat every single field three times (once as the parameter and two more times in the assignment): dataclasses does all the boilerplate for you.

You can then use the constructor as you’d expect, with keyword-arguments for each field, as well as assign to (and read) each field in the normal way:

stats = Statistics(matchId=someMatchId, ...)
...
stats.homeBallPossession = someValue
...

Leave a Reply