J'ai le test suivant
def test_employees_not_arround_for_more_than_3_rounds(self):
self.game_object.generate_workers()
people_in_list_turn_1 = self.game_object.employees[:]
self.game_object.next_turn()
self.game_object.generate_workers()
self.game_object.next_turn()
self.game_object.generate_workers()
self.game_object.next_turn()
for employee in people_in_list_turn_1:
self.assertFalse(employee in self.game_object.employees)
Fondamentalement, il génère un nombre aléatoire de travailleurs et l'ajoute à ma liste game_object.employees
. Lorsque j'appelle la fonction game_object.next_turn
, chaque employé a une variable turns_unemployed
qui contient le nombre de tours pendant lesquels il a été au chômage, une fois que cela atteint 3, le travailleur sera supprimé de la liste game_object.employees
tout à fait.
Voici le code d'implémentation de game_object.py:
def generate_workers(self):
workersToAdd = range(random.randrange(1,8))
for i in workersToAdd:
self.__employees.append(Employee())
def next_turn(self):
self.__current_turn += 1
self.__call_employees_turn_function()
self.__remove_workers_unemployed_for_3_rounds()
def __call_employees_turn_function(self):
for employee in self.employees:
employee.turn()
def __remove_workers_unemployed_for_3_rounds(self):
for employee in self.employees:
if employee.turns_unemployed >= 3:
self.employees.remove(employee)
J'ai déjà un test qui vérifie que la variable turns_unemployed
est effectivement augmentée d'une unité lorsque employee.turn()
est appelée, donc je sais que ça marche ...
La chose qui me dérange vraiment ici, c'est que mon test ne fonctionne que 50% du temps que je l'exécute, et je ne peux pas comprendre pourquoi ... Quelqu'un voit-il quelque chose qui peut causer des écarts?
Btw, exécutant Python 3.2.2
3 réponses
Vous supprimez des éléments d'une liste tout en effectuant une itération sur celle-ci dans __remove_workers_unemployed_for_3_rounds
, de sorte que la boucle ignore les éléments que vous souhaitez qu'elle supprime. Vous devez parcourir une copie de la liste.
def __remove_workers_unemployed_for_3_rounds(self):
for employee in self.employees[:]:
if employee.turns_unemployed >= 3:
self.employees.remove(employee)
Exemple:
Vous générez 2 nouveaux employés à chaque tour. Au 4ème tour, vous avez 2 employés à supprimer (les deux premiers dans la liste). Vous commencez l'itération et supprimez le premier. La liste ne contient plus que cinq éléments, mais l'itération se poursuit et examine le deuxième élément. Le problème est que le deuxième élément n'est plus le deuxième employé, mais le troisième. Le deuxième employé restera dans la liste et votre test échouera. Votre test ne fonctionne que si un seul employé est généré au premier tour.
Ne modifiez pas les conteneurs que vous parcourez.
Garder une copie pour itérer est également un vilain piratage, et cela peut vous faire trébucher plus tard dans les cas où vous devez être vraiment précis sur l'identité d'objet par rapport à l'égalité d'objet. C'est aussi tout simplement désordonné.
Il existe une manière beaucoup plus simple: adopter l'approche de programmation fonctionnelle. Créez un nouveau conteneur à l'aide de la règle "tout ce qui se trouve dans le conteneur d'origine qui ne remplit pas la condition de suppression", puis commencez à utiliser cela au lieu du conteneur d'origine.
def __remove_workers_unemployed_for_3_rounds(self):
self.employees = filter(lambda e: e.turns_unemployed < 3, self.employees)
# Or with a list comprehension:
# self.employees = [e for e in self.employees if e.turns_unemployed < 3]
# if you find that more readable.
Hugo a probablement raison sur la cause de votre problème; vous ne pouvez pas supprimer des éléments d'une liste pendant que vous l'itérez. Voici un autre problème possible, lorsque vous créez des employés, vous les mettez dans une liste appelée __employees, c'est-à-dire
def generate_workers(self):
workersToAdd = range(random.randrange(1,8))
for i in workersToAdd:
self.__employees.append(Employee())
Mais lorsque vous les parcourez plus tard, vous utilisez une liste appelée employés, c'est-à-dire
def __call_employees_turn_function(self):
for employee in self.employees:
employee.turn()
def __remove_workers_unemployed_for_3_rounds(self):
for employee in self.employees:
if employee.turns_unemployed >= 3:
self.employees.remove(employee)
Mais je ne sais pas si cela est lié à votre problème parce que je ne peux pas voir le reste de votre code - je ne suis même pas sûr que ce soit dans la même classe ou non. Vous devriez probablement publier le plus petit morceau de code complet que vous pouvez obtenir qui a le problème - de cette façon, les gens peuvent réellement exécuter votre code et reproduire le problème pour eux-mêmes.
Questions connexes
De nouvelles questions
python
Python est un langage de programmation multi-paradigme, typé dynamiquement et polyvalent. Il est conçu pour être rapide à apprendre, comprendre, utiliser et appliquer une syntaxe propre et uniforme. Veuillez noter que Python 2 est officiellement hors support à partir du 01-01-2020. Néanmoins, pour les questions Python spécifiques à la version, ajoutez la balise [python-2.7] ou [python-3.x]. Lorsque vous utilisez une variante Python (par exemple, Jython, PyPy) ou une bibliothèque (par exemple, Pandas et NumPy), veuillez l'inclure dans les balises.