Peter Nydahl
Peter Nydahl

Reputation: 119

Making attributes that calculates with the value of other attributes

I´m writing a program where you can put in home team, away team, and the result of a game. I want to make the data of the teams to change according to this and most of it does. But I can´t make the "points", "goal difference" and "played"(games) to change! This is the code i wrote so far:

class team:
    def __init__(self, name, wins, drawn, losses, goals_for, goals_against):
        self.name = name
        self.wins = int(wins)
        self.drawn = int(drawn)
        self.losses = int(losses)
        self.goals_for = int(goals_for)
        self.goals_against = int(goals_against)
        self.goals_difference = (self.goals_for - self.goals_against)
        self.points = ((self.wins * 3) + self.drawn)
        self.played = (self.wins + self.drawn + self.losses)
    def __repr__(self):
        return 'Name:{} P:{} W:{} D:{} L:{} GF:{} GA:{} GD:{} PTS:{}'.format(self.name, self.played, self.wins, self.drawn, self.losses, self.goals_for, self.goals_against, self.goals_difference, self.points)    

detroit_red_wings = team("Detroit", 1, 0, 3, 4, 5)
los_angeles_kings = team("LA", 0, 1, 4, 3, 7)
toronto_maple_leafs = team("Toronto", 1, 2, 2, 3, 6)

teamlist = [detroit_red_wings, los_angeles_kings, toronto_maple_leafs]
print(teamlist)

class data_input:
    def home_team_input(self):
        home_team = input("Type in the home team: ")
        for i in teamlist:
            if i.name == home_team:
                return i
    def away_team_input(self):           
        away_team = input("Type in the away team: ")
        for t in teamlist:
            if t.name == away_team:
                return t
    def result_input(self):
        goals_home_team = int(input("Type in the number of goals made by the home team: "))
        goals_away_team = int(input("Type in the number of goals made by the away team: "))
        return (goals_home_team, goals_away_team)

def adding_result():
    home_team = data_input.home_team_input()
    away_team = data_input.away_team_input()
    goals_home_team, goals_away_team = data_input.result_input()

    home_team.goals_for += goals_home_team
    home_team.goals_against += goals_away_team
    away_team.goals_for += goals_away_team
    away_team.goals_against += goals_home_team

    if goals_home_team > goals_away_team:
        home_team.wins += 1
        away_team.losses += 1
    if goals_home_team < goals_away_team:
        away_team.wins += 1
        home_team.losses += 1
    if goals_home_team == goals_away_team:
        home_team.drawn += 1
        away_team.drawn += 1

data_input = data_input()
adding_result()
print(teamlist)

I wrote the directions for the attributes in the __init__ method of the class team and as you can see the points depends on the wins. This all works when i create the objects but when I put in the result of the new game the points doesn't change(neither does the played or goals_difference). This surprises me because the other attributes changes when I type in result of the game in the input function.

Upvotes: 1

Views: 60

Answers (2)

miradulo
miradulo

Reputation: 29720

points, goals_difference, and played don't update because your __init__ method isn't run once again once you update the other properties - you are simply updating the other properties explicitly with their new value. Note that these properties that aren't updating are assigned by value and not by reference, there is no way for your object to know that these properties should be updated unless you do so explicitly.

There are several things you could do - a simple option could be to provide a method to update the aggregating properties, that you can call after explicitly changing the other properties. I would take all of the properties that are simply a mathematical formulation of other properties (so points, goals_difference, and played), and remove them from your initialization method. Instead call a little method like update_aggregates if you really need these properties that could look like this

def update_aggregates(self):
    self.goals_difference = (self.goals_for - self.goals_against)
    self.points = ((self.wins * 3) + self.drawn)
    self.played = (self.wins + self.drawn + self.losses)

Edit: ignoring my attempt at simplicity, the solution proposed by Steve Cohen is by all means better - using @property ensures your values will be updated appropriately at all times without the need for a manual call.

Upvotes: 0

Steve Cohen
Steve Cohen

Reputation: 712

If you update your team class to make the calculated fields properties, then the property functions will always return the correct result. You will also get an error if you try to set those properties, as they are not settable, i.e., they are the result of a calculation on other set data.

class team:
    def __init__(self, name, wins, drawn, losses, goals_for, goals_against):
        self.name = name
        self.wins = int(wins)
        self.drawn = int(drawn)
        self.losses = int(losses)
        self.goals_for = int(goals_for)
        self.goals_against = int(goals_against)

    @property
    def goals_difference(self):
        return self.goals_for - self.goals_against

    @property
    def points(self):
        return self.wins * 3 + self.drawn

    @property
    def played(self):
        return self.wins + self.drawn + self.losses

    def __repr__(self):
        return 'Name:{} P:{} W:{} D:{} L:{} GF:{} GA:{} GD:{} PTS:{}'.format(
            self.name, self.played, self.wins, self.drawn, self.losses,
            self.goals_for, self.goals_against, self.goals_difference,
            self.points)

I would also consider making the W/L/D and GF/GA initializers tupples or dictionaries rather than passing 5 variables to the initializer.

Upvotes: 3

Related Questions