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