I have defined two classes representing players and games. Also I have testing data and a function which calculate the score of a game. The problem is in such a function: It doesn't creates any global variable nor mutates any input/global data nor perform any IO... It should be a "pure" function, but when executed many times, it outputs diferent things each
Below I give a minimal reprex. The __str__(self):
and __repr__(self):
are given for better representation but are not part of the problem.
class Player:
""" Player is kind of a C - struct. It holds
a string which represents a name
a int which represent a position in a ranking
a float which represent points
"""
def __init__(self, name: str, rank: int, points: float):
self.name = name
self.rank = rank
self.points = points
def __str__(self):
"""The error doesn't happend here as far as I have checked"""
return ' - '.join([self.name, str(self.rank), str(self.points)])
def __repr__(self):
"""The error doesn't happend here as far as I have checked"""
return self.__str__()
class Game:
""" Game represents a list of Player. many methods have been remove because they don't contribute to the problem."""
def __init__(self, list_of_players = []):
self._list_of_players = list_of_players
def add_new(self, new) -> None:
"""Append a new value with type checking."""
if type(new) == Player:
self._list_of_players.append(new)
else:
raise ValueError('some error')
@property
def list_of_players(self):
"""This getter return the list in order by rank. I think the error can happend here"""
self._list_of_players.sort(key = lambda c: c.rank, reverse = True)
return self._list_of_players
def __str__(self):
"""The error doesn't happend here as far as I have checked"""
s = '\n'.join(map(str, self.list_of_players))
return s
def __repr__(self):
"""The error doesn't happend here as far as I have checked"""
return self.__str__()
The testing data and the scoring function are this
test_data = [('name0', 2), ('name1', 5), ('name2', 1), ('name3', 0), ('name4', 10), ('name5', 1)]
def calculate_score(data, weight = 1, score_func=lambda x: x + 2):
"""Calculate the score"""
total_score = 0
game = Game()
for n, i in data:
score = score_func(i)
points = (weight * score) / len(data)
game.add_new(Player(name = n, rank = i, points = points))
return game
When I execute many times calculate_score
the following happens:
>>> calculate_score(test_data)
name4 - 10 - 2.0
name1 - 5 - 1.1666666666666667
name0 - 2 - 0.6666666666666666
name2 - 1 - 0.5
name5 - 1 - 0.5
name3 - 0 - 0.3333333333333333
>>> calculate_score(test_data)
name4 - 10 - 2.0
name4 - 10 - 2.0
name1 - 5 - 1.1666666666666667
name1 - 5 - 1.1666666666666667
name0 - 2 - 0.6666666666666666
name0 - 2 - 0.6666666666666666
name2 - 1 - 0.5
name5 - 1 - 0.5
name2 - 1 - 0.5
name5 - 1 - 0.5
name3 - 0 - 0.3333333333333333
name3 - 0 - 0.3333333333333333
>>> calculate_score(test_data)
name4 - 10 - 2.0
name4 - 10 - 2.0
name4 - 10 - 2.0
name1 - 5 - 1.1666666666666667
name1 - 5 - 1.1666666666666667
name1 - 5 - 1.1666666666666667
name0 - 2 - 0.6666666666666666
name0 - 2 - 0.6666666666666666
name0 - 2 - 0.6666666666666666
name2 - 1 - 0.5
name5 - 1 - 0.5
name2 - 1 - 0.5
name5 - 1 - 0.5
name2 - 1 - 0.5
name5 - 1 - 0.5
name3 - 0 - 0.3333333333333333
name3 - 0 - 0.3333333333333333
name3 - 0 - 0.3333333333333333
The game
variable should be deleted after the execution of calculate_score
isn't it?.
I'm using python 3.6 in jupyterlab