为什么这段代码只能工作一半的时间?

时间:2011-11-27 03:49:30

标签: python python-3.x

我有以下测试

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)

基本上,它会生成随机数量的工作人员并将其添加到我的game_object.employees列表中。 当我调用game_object.next_turn函数时,每个员工都有一个turns_unemployed变量来保存他们失业的轮数,一旦达到3,工人就会从game_object.employees列表中删除共

以下是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)

我已经有一个测试,当turns_unemployed被调用时,检查employee.turn()变量实际上增加了1,所以我知道它有效...

在这里真正让我感到困惑的是,我的测试只有50%的时间用于运行它,我无法弄清楚为什么......任何人都会看到任何可能导致任何差异的事情?

顺便说一下,运行Python 3.2.2

3 个答案:

答案 0 :(得分:4)

您正在__remove_workers_unemployed_for_3_rounds中对项目进行迭代时从列表中删除项目,因此循环会跳过您希望删除的项目。您需要迭代列表的副本。

def __remove_workers_unemployed_for_3_rounds(self):
    for employee in self.employees[:]:
        if employee.turns_unemployed >= 3:
            self.employees.remove(employee)

示例:

每回合生成2名新员工。在第4回合,你有2名员工要删除(列表中的两个)。您开始迭代并删除第一个。该列表现在只有五个项目,但迭代继续并查看第二个项目。问题是第二项不再是第二名员工,而是第三项。第二名员工将留在列表中,您的测试将失败。只有在第一个回合中只生成一名员工时,您的测试才有效。

答案 1 :(得分:3)

雨果对于导致你问题的原因可能是正确的;在迭代它时,您无法从列表中删除项目。这是另一个可能的问题,当你创建员工时,你把它们放在一个名为__employees的列表中,即

def generate_workers(self):
    workersToAdd = range(random.randrange(1,8))
    for i in workersToAdd:
        self.__employees.append(Employee())

但是当你稍后迭代它们时,你会使用一个名为employees的列表,即

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)

但我不知道这是否与您的问题有关,因为我看不到您的其余代码 - 我甚至不确定这些是否属于同一类。您应该发布可以获得的最小的完整代码 - 这样人们可以实际运行代码并为自己重现问题。

答案 2 :(得分:3)

不要修改您正在迭代的容器。

保持副本迭代也是一个丑陋的黑客攻击,如果你必须非常准确地了解对象身份与对象相等,它可能会让你绊倒。它也只是简单的混乱。

有一种更简单的方法:采用函数式编程方法。使用规则“从原始容器中不符合要删除条件的所有内容”创建一个新容器,然后开始使用该容器而不是原始容器。

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.